import opennlp.tools.postag.POSModel;
import opennlp.tools.postag.POSTaggerME;
import opennlp.tools.sentdetect.SentenceDetectorME;
import opennlp.tools.sentdetect.SentenceModel;
import opennlp.tools.tokenize.Tokenizer;
import opennlp.tools.tokenize.TokenizerME;
import opennlp.tools.tokenize.TokenizerModel;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * Created by shumailovr on 24.02.15.
 */
public class BookAnalysator {

    //Character counter
    public int countCharacters(String input) {
        String newInput = input.replaceAll("[^a-zA-Z0-9]+", "");
        return newInput.length();
    }

    //Counts different persons number in the book
    public int countPersonsNames(ArrayList<String[]> tokenizedSentences) {
        NamedEntityFinder personNameFinder = new NamedEntityFinder();
        personNameFinder.readModel("models/en-ner-person.bin");
        int namesNum = personNameFinder.readUniqueNamedEntities(tokenizedSentences);
        return namesNum;
    }

    //Counts dates number in the book
    public int countDates(ArrayList<String[]> tokenizedSentences) {
        NamedEntityFinder personNameFinder = new NamedEntityFinder();
        personNameFinder.readModel("models/en-ner-date.bin");
        int dates = personNameFinder.countNamedEntities(tokenizedSentences);
        return dates;
    }

    //Counts different organizations number in the book
    public int countOrganizationsNames(ArrayList<String[]> tokenizedSentences) {
        NamedEntityFinder personNameFinder = new NamedEntityFinder();
        personNameFinder.readModel("models/en-ner-organization.bin");
        int organizationsNum = personNameFinder.readUniqueNamedEntities(tokenizedSentences);
        return organizationsNum;
    }

    //Counts different locations number in the book
    public int countLocationsNames(ArrayList<String[]> tokenizedSentences) {
        NamedEntityFinder personNameFinder = new NamedEntityFinder();
        personNameFinder.readModel("models/en-ner-location.bin");
        int locationsNum = personNameFinder.readUniqueNamedEntities(tokenizedSentences);
        return locationsNum;
    }


    /**
     * Part of Speech word finder
     * @param modelPath
     * @param tokenizedSentences
     * @return
     */
    public Map<String, Integer> getPOSwordsNumber(String modelPath, ArrayList<String[]> tokenizedSentences) {

        InputStream modelIn = null;

        try {
            modelIn = new FileInputStream(modelPath);
            POSModel posModel = new POSModel(modelIn);

            POSTaggerME tagger = new POSTaggerME(posModel);

            Map<String, Integer> map = new HashMap<String, Integer>();

            int adjectives = 0;
            int nouns = 0;
            int verbs = 0;

            for (String[] sentence : tokenizedSentences) {
                String[] taggedSentence = tagger.tag(sentence);

                adjectives += Collections.frequency(Arrays.asList(taggedSentence), "JJ");
                adjectives += Collections.frequency(Arrays.asList(taggedSentence), "JJR");
                adjectives += Collections.frequency(Arrays.asList(taggedSentence), "JJS");

                nouns += Collections.frequency(Arrays.asList(taggedSentence), "NN");
                nouns += Collections.frequency(Arrays.asList(taggedSentence), "NNS");
                nouns += Collections.frequency(Arrays.asList(taggedSentence), "NNP");
                nouns += Collections.frequency(Arrays.asList(taggedSentence), "NNPS");

                verbs += Collections.frequency(Arrays.asList(taggedSentence), "VB");
                verbs += Collections.frequency(Arrays.asList(taggedSentence), "VBD");
                verbs += Collections.frequency(Arrays.asList(taggedSentence), "VBG");
                verbs += Collections.frequency(Arrays.asList(taggedSentence), "VBN");
                verbs += Collections.frequency(Arrays.asList(taggedSentence), "VBP");
                verbs += Collections.frequency(Arrays.asList(taggedSentence), "VBZ");
            }

            map.put("adjectivesNum", adjectives);
            map.put("nounsNum", nouns);
            map.put("verbsNum", verbs);

            return map;
        } catch (IOException e) {
            // Model loading failed, handle the error
            e.printStackTrace();
        } finally {
            if (modelIn != null) {
                try {
                    modelIn.close();
                } catch (IOException e) {
                }
            }
        }

        return new HashMap<String, Integer>();
    }

    /**
     * Sentence detector
     * @param modelLocation
     * @param input
     * @return
     */
    public String[] detectSentences(String modelLocation, String input) {

        InputStream modelIn = null;
        try {
            modelIn = new FileInputStream(modelLocation);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            SentenceModel sModel = new SentenceModel(modelIn);
            SentenceDetectorME sentenceDetector = new SentenceDetectorME(sModel);
            String sentences[] = sentenceDetector.sentDetect(input);

            return sentences;
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if (modelIn != null) {
                try {
                    modelIn.close();
                }
                catch (IOException e) {
                }
            }
        }

        return new String[0];
    }

    /**
     * Average sentence length calculator
     * @param words
     * @param sentences
     * @return
     */
    public float calcAverSentenceLength(int words, int sentences) {
        return (Math.round((words / sentences) * 100000) / 100000);
    }

    //Calculates positiveness of the book
    public float calcPositiveness(ArrayList<String[]> tokenizedSentences) {
        CSVReader csvReader = new CSVReader();
        //This uses stemmed words valences and positiveness calculator to calculate text positiveness
        csvReader.readRatingsCSV("affectiveRatings/newAffectiveRatings.csv");
        ArrayList<String> stemmedWords = csvReader.getStemmedWords();
        ArrayList<Float> valences = csvReader.getValences();

        SentimentCalculator sentimentCalculator = new SentimentCalculator();
        sentimentCalculator.setStemmedWords(stemmedWords);
        sentimentCalculator.setValences(valences);
        float positiveness = sentimentCalculator.evaluate(tokenizedSentences);
        //        System.out.println(positiveness);
        return positiveness;
    }

    /**
     * Tokenizes sentences
     * @param modelPath
     * @param inputSentences
     * @return
     */
    public ArrayList<String[]> tokenize(String modelPath, String[] inputSentences) {
        InputStream modelIn = null;

        try {
            modelIn = new FileInputStream(modelPath);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        try {
            TokenizerModel model = new TokenizerModel(modelIn);
            Tokenizer tokenizer = new TokenizerME(model);

            ArrayList tokenizedSentences = new ArrayList();

            for (String sentence : inputSentences) {
                String sentenceTokens[] = tokenizer.tokenize(sentence);
                tokenizedSentences.add(sentenceTokens);
            }

            return tokenizedSentences;
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if (modelIn != null) {
                try {
                    modelIn.close();
                }
                catch (IOException e) {
                }
            }
        }
        return new ArrayList<String[]>();
    }

    /**
     * Counts number of words in a book
     * @param tokenizedSentences
     * @return
     */
    public int countWords(ArrayList<String[]> tokenizedSentences) {
        int words = 0;
        for (String[] sentence : tokenizedSentences) {
            for (String token : sentence) {
                if (Character.isLetter(token.charAt(0)) || Character.isDigit(token.charAt(0))) {
                    words += 1;
                }
            }
        }
        return words;
    }

    /**
     * Counts words average length
     * @param characters
     * @param words
     * @return
     */
    public float calcAverWordLength(int characters, int words) {
        return (Math.round((characters / words) * 100000) / 100000);
    }



}
