package edu.caltech.cs2.textgenerator;

import edu.caltech.cs2.datastructures.LinkedDeque;
import edu.caltech.cs2.interfaces.IDeque;

import java.util.Arrays;
import java.util.Iterator;

public class NGram implements Iterable<String>, Comparable<NGram> {
    public static final String NO_SPACE_BEFORE = ",?!.-,:'";
    public static final String NO_SPACE_AFTER = "-'><=";
    public static final String REGEX_TO_FILTER = "”|\"|“|\\(|\\)|\\*";
    public static final String DELIMITER = "\\s+|\\s*\\b\\s*";
    private IDeque<String> data;
    private static int[] primes = new int[1];

    public static String normalize(String s) {
        return s.replaceAll(REGEX_TO_FILTER, "").strip();
    }

    public NGram(IDeque<String> x) {
        this.data = new LinkedDeque<>();
        for (String word : x) {
            this.data.add(word);
        }
    }

    public NGram(String data) {
        this(normalize(data).split(DELIMITER));
    }

    public NGram(String[] data) {
        this.data = new LinkedDeque<>();
        for (String s : data) {
            s = normalize(s);
            if (!s.isEmpty()) {
                this.data.addBack(s);
            }
        }
    }

    public NGram next(String word) {
        String[] data = new String[this.data.size()];
        Iterator<String> dataIterator = this.data.iterator();
        dataIterator.next();
        for (int i = 0; i < data.length - 1; i++) {
            data[i] = dataIterator.next();
        }
        data[data.length - 1] = word;
        return new NGram(data);
    }

    public String toString() {
        String result = "";
        String prev = "";
        for (String s : this.data) {
            result += ((NO_SPACE_AFTER.contains(prev) || NO_SPACE_BEFORE.contains(s) || result.isEmpty()) ? "" : " ") + s;
            prev = s;
        }
        return result.strip();
    }

    @Override
    public Iterator<String> iterator() {
        return this.data.iterator();
    }

    @Override
    public int compareTo(NGram other) {
        NGram copy = new NGram(other.data);
        if (this.equals(other)){
            return 0;
        }
        if (data.size() == other.data.size()){
            for (String word : data){
                String otherWord = copy.data.removeFront();
                if (!word.equals(otherWord)){
                    return word.compareTo(otherWord);
                }
            }
        }
        return this.data.size() - other.data.size();
    }


    @Override
    public boolean equals(Object o) {

        NGram copy = new NGram(((NGram)o).data);
        if (!(o instanceof NGram)) {
            return false;
        }
        if (copy.data == null && this.data != null){
            return false;
        }else if (copy.data != null && this.data == null){
            return false;
        }else if (copy.data == null && this.data == null){
            return true;
        }

        if (copy.data.size() != this.data.size()){
            return false;
        }

        for (String word : data){
            if (!word.equals(copy.data.removeFront())){
                return false;
            }

        }
        return true;
    }

    @Override
    public int hashCode() {
        if (this.data == null){
            return 0;
        }
//        int[] arrayData = new int[data.size()];

        int hashCode = 0;
        for (String word : data){
            hashCode *= 31;
            hashCode += word.hashCode();

        }
        return hashCode;
//        int counter = 0;
//        for (String word : this.data){
//            int wordVal = 0;
//            for (char c : word.toCharArray()){
//                wordVal += c;
//            }
//            arrayData[counter] = wordVal;
//            counter ++;
//        }
//
//        return horners(arrayData);

    }

//    private int horners(int[] arrayData){
//        int length = arrayData.length;
//        if (arrayData.length == 2){
//            return arrayData[0] + 31*(arrayData[1]);
//        }else if (arrayData.length > 2){
//            return arrayData[0] + 31*(horners(Arrays.copyOfRange(arrayData, 1, length)));
//        } else {
//            return 0;
//        }
//    }
}