import processing.core.PApplet;
import java.util.*;
public class DeweyLetterMatrix {
  
    // Counter that will track number of transactions
    int[][] count; 
    //Dewey Specifications
    String deweyBeg;  // Begining dewey number
    String deweyEnd;  // Ending dewey number
    int deweyStep; // Range of dewey numbers to capture in each row
    // String containing all expected first characters  
    String firstCharacters;
    String[] deweyClassesStr; // Contains the range of dewey Classes
    int numDeweyClasses; // Number of dewey classes in deweyClassesStr
    int numFirstCharacters;
    Long deweyBegVal;
    Long deweyEndVal;
    PApplet parent;
    
    //Constructor
    DeweyLetterMatrix(PApplet p)
    {
       parent = p;
       deweyBeg  = "000";
       deweyEnd  = "999";
       deweyStep = 10;
       firstCharacters = "abcdefghijklmnopqrstuvwxyz0123456789";
       
       deweyBegVal = deweyStr2Long(deweyBeg);
       deweyEndVal = deweyStr2Long(deweyEnd);
       
       numDeweyClasses = (int) (((deweyEndVal - deweyBegVal + 1)/deweyStep)+1);
       deweyClassesStr = new String[numDeweyClasses];
       for (Long i = deweyBegVal; i < deweyEndVal; i+=deweyStep)
       {
          int curIdx = (int)(i/deweyStep);
          deweyClassesStr[curIdx] = deweyLong2Str(i);
       }
       deweyClassesStr[numDeweyClasses-1] = "No Dewey";
       
       numFirstCharacters = firstCharacters.length()+1;
       count = new int[numFirstCharacters][numDeweyClasses];
    }
    
    void updateCount(String deweyStr, Character firstChar)
    {
      if(firstChar != null)
      {
        count[getFirstCharactersIdx(firstChar)][getDeweyClassesStrIdx(deweyStr)]++;
      }
    }
    
    String getPrimaryDeweyStr(String fullDeweyStr)
    {
      if(isNoDewey(fullDeweyStr))
      {
        return null;
      }
      else
      {
        String subStr = fullDeweyStr.substring(0,3);
        try
        {
          deweyStr2Long(subStr);
          return subStr;
        }
        catch(Exception e)
        {
          System.out.println("Considering dewey str " + fullDeweyStr + " to be No Dewey");
          return null;
        }
      }
    }
    
    // Index into the deweyClassesStr 
    int getDeweyClassesStrIdx(String deweyStr)
    {
      // Non Dewey Strings: null, empty and blank or white spaces
      if(isNoDewey(deweyStr))
      {
        return(numDeweyClasses-1);
      }
      else
      {
        return (int) (Math.floor(deweyStr2Long(deweyStr)/deweyStep));
      }
    }
    
    boolean isNoDewey(String deweyStr)
    { 
        if(deweyStr == null || deweyStr.length()==0 || deweyStr.trim().length() == 0)
        {
          return true;
        }
        return false;
    }
    
    // Index into the firstCharacters 
    int getFirstCharactersIdx(Character firstChar)
    {
      int idx = firstCharacters.indexOf(Character.toLowerCase(firstChar));
      if(idx != -1)
      {
        return idx;
      }
      else
      {
        return(numFirstCharacters-1);
      }
    }
    
    /***************************************************************/
    /* Function that doesn't consider includeKeyWords              */
    /***************************************************************/
    Character getFirstCharacter(String inputString, String[] ignoreKeyWords)
    {
      Character firstChar = null;
    
    try
    {
      //convert input string to lowercase
      inputString = inputString.toLowerCase();
      String delims = "[ \t\n,:;-]+"; //NOTE: add all possible delimiters here as a regular expression
      String[] inputTokens = inputString.split(delims); //get tokens
    
      Set<String> ignoreSet = new HashSet<String>();
      for (int i = 0; i < ignoreKeyWords.length; i++)
      {
          ignoreSet.add(ignoreKeyWords[i].toLowerCase()); //takes care of lower/upper case issue
      }
      
      for (int j = 0; j < inputTokens.length; j++)
      {
          if (!ignoreSet.contains(inputTokens[j]))
          {   
              firstChar = inputTokens[j].charAt(0);
              break;
          }    
      }
      return firstChar;
    }
    catch(Exception e)
    {
      System.out.println("Indeterminate first character, assuming # instead");
      firstChar = '#';
      return firstChar;
    }
    
      
    }
    
    /***************************************************************/
    /* Function that considers includeKeyWords                     */
    /***************************************************************/
    Character getFirstCharacter(String inputString, String[] ignoreKeyWords, String[] includeKeyWords)
    {
      Character firstChar = null; 
    try
    {
      //convert input string to lowercase
      inputString = inputString.toLowerCase();
      String delims = "[ \t\n,:;-]+"; //NOTE: add all possible delimiters here as a regular expression
      String[] inputTokens = inputString.split(delims); //get tokens
      
      Set<String> ignoreSet = new HashSet<String>();
      for (int i = 0; i < ignoreKeyWords.length; i++)
      {
          ignoreSet.add(ignoreKeyWords[i].toLowerCase()); //takes care of lower/upper case issue
      }
      
      Set<String> includeSet = new HashSet<String>();
      for (int i = 0; i < includeKeyWords.length; i++)
      {
          includeSet.add(includeKeyWords[i].toLowerCase()); //takes care of lower/upper case issue
      }
    
      for (int i = 0; i < inputTokens.length; i++)
      {
          if (includeSet.contains(inputTokens[i])) //input has include word
          {
              for (int j = 0; j < inputTokens.length; j++)
              {
                  if (!ignoreSet.contains(inputTokens[j]))
                  {   
                      firstChar = inputTokens[j].charAt(0);
                      break;
                  }    
              }
              break; //for the outer for loop
          }
      }
      return firstChar;
    }
    catch(Exception e)
    {
      System.out.println("Indeterminate first character, assuming # instead");
      firstChar = '#';
      return firstChar;
    }
    }
    
    
    // Print the value of the dewey number
    void printDeweyValue(String deweyStr)
    {
      Long deweyVal = deweyStr2Long(deweyStr);
      System.out.println("Dewey Value "+deweyVal);
      String deweyStrMod = deweyLong2Str(deweyVal); 
      System.out.println("Dewey String "+deweyStrMod);
    }
    
    // Convert the dewey string to an integer
    Long deweyStr2Long(String deweyStr)
    {
      Long deweyVal = Long.valueOf(deweyStr);
      return deweyVal;
    }
    
    // Convert the dewey integer to a string
    String deweyLong2Str(Long deweyVal)
    {
      String deweyStr = addLeadingZeros(deweyVal);
      return deweyStr;
    }
    
    // Add leading zeros to dewey number if it has only one or two digits
    String addLeadingZeros(Long number) 
    {
    	String s= PApplet.nf(number.intValue(), 3);
    	return s;
      //String s= "00"+number; // 2 zeros prepended
      //return s.substring(s.length()-3); // keep the rightmost 3 chars
    }
    
    // Print formatted 2D matrix to terminal
    void printDeweyMatrix()
    {
      for(int i = 0; i < numFirstCharacters; i++)
      {
        for(int j = 0; j < numDeweyClasses; j++)
        {
        	System.out.print(count[i][j]+" ");
        }
        System.out.println(" ");
      }
    }
    
    // Save the DeweyMatrix to a text file
    void saveDeweyMatrix(String fileName)
    {
      String[] deweyMatrixStr = new String[numFirstCharacters*numDeweyClasses];
      int k=0;
      for(int i = 0; i < numFirstCharacters; i++)
      {
        for(int j = 0; j < numDeweyClasses; j++)
        {
          deweyMatrixStr[k] = String.valueOf(count[i][j]);
          k++;
        }
      }
      parent.saveStrings(fileName, deweyMatrixStr);
    }
    
    // Load the DeweyMatrix from a text file
    void loadDeweyMatrix(String fileName)
    {
      String[] deweyMatrixStr = parent.loadStrings(fileName);
      int k=0;
      for(int i = 0; i < numFirstCharacters; i++)
      {
        for(int j = 0; j < numDeweyClasses; j++)
        {
          Integer temp=0;
          temp = Integer.valueOf(deweyMatrixStr[k++]);
          count[i][j] = temp;
        }
      }
    }
    
    StatCalc computeDeweyMatrixStat()
    {
      StatCalc deweyMatrixStat = new StatCalc(); //Dewey Matrix Statistics
      for(int i = 0; i < numFirstCharacters; i++)
      {
        for(int j = 0; j < numDeweyClasses; j++)
        {
          if(count[i][j] != 0)
          {
            deweyMatrixStat.enter(count[i][j]);
          } 
        }
      }
      return deweyMatrixStat;
    }
    
    StatCalc computeDeweyOnlyMatrixStat()
    {
      StatCalc deweyMatrixStat = new StatCalc(); //Dewey Matrix Statistics
      for(int i = 0; i < numFirstCharacters; i++)
      {
        for(int j = 0; j < numDeweyClasses-1; j++)
        {
          if(count[i][j] != 0)
          {
            deweyMatrixStat.enter(count[i][j]);
          } 
        }
      }
      return deweyMatrixStat;
    }
    
    StatCalc computeDeweyMatrixStatRow(int rowIdx)
    {
      StatCalc deweyMatrixStat = new StatCalc(); //Dewey Matrix Statistics
      int i = rowIdx;
      for(int j = 0; j < numDeweyClasses; j++)
      {
        deweyMatrixStat.enter(count[i][j]);
      }
      return deweyMatrixStat;
    }
    
    StatCalc computeDeweyMatrixStatCol(int colIdx)
    {
      StatCalc deweyMatrixStat = new StatCalc(); //Dewey Matrix Statistics
      int j = colIdx;
      for(int i = 0; i < numFirstCharacters; i++)
      {
        deweyMatrixStat.enter(count[i][j]);
      }
      return deweyMatrixStat;
    }
    
    double getDeweyMatrixMean()
    {
      StatCalc deweyMatrixStat = computeDeweyMatrixStat();
      return deweyMatrixStat.getMean();
    }
    
    double getDeweyMatrixSD()
    {
      StatCalc deweyMatrixStat = computeDeweyMatrixStat();
      return deweyMatrixStat.getStandardDeviation();
    }
      
    int getDeweyMatrixMax()
    {
      StatCalc deweyMatrixStat = computeDeweyMatrixStat();
      return (int)deweyMatrixStat.getMax();
    }  
    
    int getDeweyMatrixMin()
    {
      StatCalc deweyMatrixStat = computeDeweyOnlyMatrixStat();
      return (int)deweyMatrixStat.getMin();
    }
       
    int getDeweyMatrixSum()
    {
      StatCalc deweyMatrixStat = computeDeweyMatrixStat();
      return (int)deweyMatrixStat.getSum();
    }
    
    double getDeweyOnlyMatrixMean()
    {
      StatCalc deweyMatrixStat = computeDeweyOnlyMatrixStat();
      return deweyMatrixStat.getMean();
    }
    
    double getDeweyOnlyMatrixSD()
    {
      StatCalc deweyMatrixStat = computeDeweyOnlyMatrixStat();
      return deweyMatrixStat.getStandardDeviation();
    }
    
    int getDeweyOnlyMatrixMax()
    {
      StatCalc deweyMatrixStat = computeDeweyOnlyMatrixStat();
      return (int)deweyMatrixStat.getMax();
    }
    
    int getDeweyOnlyMatrixMin()
    {
      StatCalc deweyMatrixStat = computeDeweyMatrixStat();
      return (int)deweyMatrixStat.getMin();
    }
    
    int getDeweyOnlyMatrixSum()
    {
      StatCalc deweyMatrixStat = computeDeweyOnlyMatrixStat();
      return (int)deweyMatrixStat.getSum();
    }
    
    
}
