package app.crossword.yourealwaysbe.puz;

import java.util.Arrays;
import java.util.logging.Logger;

public class Note {
    private static final Logger LOG
        = Logger.getLogger(Note.class.getCanonicalName());

    private static final char BLANK_CHAR = Box.BLANK.charAt(0);

    private String scratch;
    private String text;
    private String anagramSource;
    private String anagramSolution;

    public Note(String scratch,
                String text,
                String anagramSource,
                String anagramSolution) {
        this.text = text;
        this.scratch = scratch;
        this.anagramSource = anagramSource;
        this.anagramSolution = anagramSolution;
    }

    public Note(int wordLength) {
        this.scratch = createBlankString(wordLength);
    }

    public String getText() {
        return text;
    }

    public String getScratch() {
        return scratch;
    }

    public String getAnagramSource() {
        return anagramSource;
    }

    public String getAnagramSolution() {
        return anagramSolution;
    }

    /**
     * Return null if the string is full of blanks
     */
    public String getCompressedScratch() {
        if (isBlankString(scratch))
            return null;
        else
            return scratch;
    }

    /**
     * Return null if the string is full of blanks
     */
    public String getCompressedAnagramSource() {
        if (isBlankString(anagramSource))
            return null;
        else
            return anagramSource;
    }

    /**
     * Return null if the string is full of blanks
     */
    public String getCompressedAnagramSolution() {
        if (isBlankString(anagramSolution))
            return null;
        else
            return anagramSolution;
    }

    public boolean isEmpty() {
        return (text == null || text.length() == 0) &&
               (scratch == null || scratch.trim().length() == 0) &&
               (anagramSource == null || anagramSource.trim().length() == 0) &&
               (anagramSolution == null || anagramSolution.trim().length() == 0);
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof Note) {
            Note n = (Note) o;
            return safeStringEquals(this.text, n.text) &&
                   safeStringEquals(this.scratch, n.scratch) &&
                   safeStringEquals(this.anagramSource, n.anagramSource) &&
                   safeStringEquals(this.anagramSolution, n.anagramSolution);
        }
        return false;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;

        result = (prime * result) + (text == null ? 0 : text.hashCode());
        result = (prime * result) +
                 (scratch == null ? 0 : scratch.hashCode());
        result = (prime * result) +
                 (anagramSource == null ? 0 : anagramSource.hashCode());
        result = (prime * result) +
                 (anagramSolution == null ? 0 : anagramSolution.hashCode());

        return result;
    }


    private static final boolean safeStringEquals(String s1, String s2) {
        if (s1 == null) {
            return (s2 == null);
        } else {
            return s1.equals(s2);
        }
    }

    private String createBlankString(int len) {
        if (len == 0) return "";

        char[] padding = new char[len];
        Arrays.fill(padding, BLANK_CHAR);
        return new String(padding);
    }

    private boolean isBlankString(String s) {
        if (s == null)
            return true;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) != BLANK_CHAR)
                return false;
        }
        return true;
    }

    public Note withScratch(String scratch) {
        return new Note(
            scratch,
            getText(),
            getAnagramSource(),
            getAnagramSolution()
        );
    }

    public Note withText(String text) {
        return new Note(
            getScratch(),
            text,
            getAnagramSource(),
            getAnagramSolution()
        );
    }

    public Note withAnagramSource(String anagramSource) {
        return new Note(
            getScratch(),
            getText(),
            anagramSource,
            getAnagramSolution()
        );
    }

    public Note withAnagramSolution(String anagramSolution) {
        return new Note(
            getScratch(),
            getText(),
            getAnagramSource(),
            anagramSolution
        );
    }

    public Note withAnagram( String anagramSource, String anagramSolution) {
        return new Note(
            getScratch(),
            getText(),
            anagramSource,
            anagramSolution
        );
    }

    /**
     * Create a copy with updated scratch letter
     *
     * Returns same object if scratch is currently null or pos is out of
     * range
     */
    public Note withScratchLetter(int pos, char letter) {
        String letterText = Character.toString(letter);
        String newScratchText;

        if (scratch == null || pos < 0 || pos >= scratch.length()) {
            LOG.warning("Can't set scratch letter because scratch text not created");
            return this;
        }

        int len = scratch.length();
        if (pos == 0) {
            newScratchText = letterText + scratch.substring(1);
        } else if (pos == len - 1) {
            newScratchText = scratch.substring(0, pos) + letterText;
        } else {
            newScratchText = scratch.substring(0, pos) + letterText + scratch.substring(pos + 1);
        }

        return new Note(
            newScratchText,
            getText(),
            getAnagramSource(),
            getAnagramSolution()
        );
    }

    /**
     * Return new note with scratch letter at pos deleted
     *
     * Returns same object if pos out of range
     */
    public Note withDeleteScratchLetterAt(int pos) {
        return withScratchLetter(pos, BLANK_CHAR);
    }

    @Override
    public String toString() {
        return "Scratch: " + getScratch() + "; "
            + "Text: " + getText() + "; "
            + "Anagram source: " + getAnagramSource() + "; "
            + "Anagram solution: " + getAnagramSolution();
    }
}
