package com.android.Algorithm;
import java.util.Vector;
import java.io.InputStream;
import java.io.InputStreamReader;

import android.util.Log;

import com.android.Algorithm.*;
//import com.android.testbtspp2_1.*;
import com.android.MathClass.*;
import com.android.application.*;
import com.android.main.*;
/**
 * input: 4CH EEG data
 * output: array, with correlation of displayRangeFrom Hz to (displayRangeTo/4)+1 in it
 * Note: minimum points to CCA-> 256pts.
 */
public class CanonicalCorrelationAnalysis {
    private int sRate;
    private Vector SIN;
    private Vector COS;
    protected int displayRangeFrom;
    private int dataBasePoints,tagOfPoints;//tagOfPoints: depent on how many points be needed by displayCanvas
    /**
     * variables below are used in CCA function.
    ********************************************/
    private double [][] z,c;
    private double[]sin,cos;
    private Matrix Z,C,Cxx,Cxy,Cyx,Cyy,invCyy;
    private EigenvalueDecomposition eig;
    double[][]rr;
    double tempMax;
    private final boolean DEBUG = true;
    private final String TAG = "CanonicalCorrelationAnalysis";
    //******************************************

    public CanonicalCorrelationAnalysis() {
        this.sRate = Buffer.sRate;
        this.dataBasePoints = sRate*8;
        SIN = new Vector();
        COS = new Vector();

        //createSinCosDatabase2();
        createSinCosDataBase();
        displayRangeFrom = 32;//9hz
        tagOfPoints = 256;//will be modified in the argument of CCA function.
        /**
         * variables below are used in CCA function.
        ********************************************/
        z = new double[dataBasePoints][6];
        c = new double[6][6];
        Z = new Matrix(z);
        C = new Matrix(c);
        rr = new double[6][6];
        tempMax = 0;
        //******************************************
        /*this.tagOfPoints = 128*6;
        double[][] EEG = this.readLineFromFile("10hz-sig-2.txt");

        double []ch1 = new double[tagOfPoints];
        double []ch2 = new double[tagOfPoints];
        double []ch3 = new double[tagOfPoints];
        double []ch4 = new double[tagOfPoints];
        for(int i=0;i<tagOfPoints;i++){
            ch1[i] = EEG[i][0];
            ch2[i] = EEG[i][1];
            ch3[i] = EEG[i][2];
            ch4[i] = EEG[i][3];
        }
        double[] a = this.CCA(ch1,ch2,ch3,ch4,tagOfPoints);
        for(int i=0;i<a.length;i++){
            System.out.println("a["+i+"]= "+a[i]);
        }*/
    }
    public synchronized void setDisplayRangeFrom(int newRange){
        if (newRange >= 1) {
            newRange = (newRange -1 ) * 4;
            this.displayRangeFrom = newRange;
        }
    }
    public synchronized double[] CCA(double[] ch1,double[]ch2,double[]ch3,double[]ch4,int numberOfPoints){
        double[] result = new double[128];
        if(numberOfPoints <= dataBasePoints){
            this.tagOfPoints = numberOfPoints;
            try {
                for (int i = 0; i < tagOfPoints; i++) {
                    z[i][0] = ch1[i];
                    z[i][1] = ch2[i];
                    z[i][2] = ch3[i];
                    z[i][3] = ch4[i];
                }
                for (int i = 0; i < 4; i++) {
                    for (int j = 0; j < 4; j++) {
                        c[i][j] = this.getCov(Z, i, Z, j,tagOfPoints);
                    }
                }
                for (int index = displayRangeFrom;
                                 index < (displayRangeFrom + 12); index++) {
                    sin = (double[])this.SIN.elementAt(index);
                    cos = (double[])this.COS.elementAt(index);
                    for (int i = 0; i < tagOfPoints; i++) {
                        z[i][4] = sin[i];
                        z[i][5] = cos[i];
                    }
                    for (int i = 0; i < 6; i++) {
                        for (int j = 4; j < 6; j++) {
                            c[i][j] = this.getCov(Z, i, Z, j, tagOfPoints);
                            /*c[i][j] = this.getCov(Z.getMatrix(0,
                                    (numberOfPointsToCCA - 1), i, i).
                                                  getColumnPackedCopy(),
                                                  Z.getMatrix(0,
                                    (numberOfPointsToCCA - 1), j, j).
                                                  getColumnPackedCopy());*/
                        }
                    }
                    Cxx = C.getMatrix(0, 3, 0, 3);
                    Cxy = C.getMatrix(0, 3, 4, 5);
                    Cyx = Cxy.transpose();
                    Cyy = C.getMatrix(4, 5, 4, 5);
                    invCyy = Cyy.inverse();
                    //Matrix temp = Cxx.inverse().times(Cxy).times(invCyy).times(Cyx);
                    eig = new EigenvalueDecomposition(Cxx.
                            inverse().times(Cxy).times(
                                    invCyy).times(Cyx));
                    //Matrix Wx = eig.getV();
                    Matrix r = eig.getD();
                    rr = r.getArray();
                    tempMax = 0;
                    for (int i = 0; i < rr.length; i++) {
                        for (int j = 0; j < (rr[0].length); j++) {
                            tempMax = Math.max(tempMax, rr[i][j]);
                            //temp = Math.max(temp,Math.pow(tt[i][j],2));
                        }
                    }
                    //if(!(Math.sqrt(r.get(0, 0))>0 && Math.sqrt(r.get(0, 0))<1))
                    //    this.myeeg.discoverylistener.log("***r= "+r.get(0,0)+" ***");
                    result[index] = Math.sqrt(tempMax);

                }
            } catch (Exception e) {
                if(DEBUG)Log.e(TAG, "Excep: CCA()--"+e.getMessage());
            }

        }
        else
            if(DEBUG)Log.e(TAG, "ch1.length!=numberOfPoints");
        return result;
    }
    /**
     * The comparision will be from "displayRangeFrom" to "displayRangeFrom+11"
     * hz      1 1.25 1.5 1.75  2  2.25  ...
     * result  0  1    2   3    4   5    ...
     */
    /*public synchronized double[] CCA(double[] ch1,double[]ch2,double[]ch3,double[]ch4){
        double[] result = new double[sRate];
        try{
            for (int i = 0; i < dataBasePoints; i++) {
                z[i][0] = ch1[i];
                z[i][1] = ch2[i];
                z[i][2] = ch3[i];
                z[i][3] = ch4[i];
            }
            for(int i=0;i<4;i++){
                for(int j=0;j<4;j++){
                    c[i][j] = this.getCov(Z,i,Z,j);
                }
            }
            for (int index = displayRangeFrom;index< (displayRangeFrom+12);index++) {
                sin = (double[])this.SIN.elementAt(index);
                cos = (double[])this.COS.elementAt(index);
                for (int i = 0; i < tagOfPoints; i++) {
                    z[i][4] = sin[i];
                    z[i][5] = cos[i];
                }
                for (int i = 0; i < 6; i++) {
                    for (int j = 4; j < 6; j++) {
                        c[i][j] = this.getCov(Z,i,Z,j);
                    }
                }
                Cxx = C.getMatrix(0, 3, 0, 3);
                Cxy = C.getMatrix(0, 3, 4, 5);
                Cyx = Cxy.transpose();
                Cyy = C.getMatrix(4, 5, 4, 5);
                invCyy = Cyy.inverse();
                //Matrix temp = Cxx.inverse().times(Cxy).times(invCyy).times(Cyx);
                eig = new EigenvalueDecomposition(Cxx.
                                                  inverse().times(Cxy).times(
                        invCyy).times(Cyx));
                //Matrix Wx = eig.getV();
                Matrix r = eig.getD();
                rr = r.getArray();
                tempMax=0;
                for(int i=0;i<rr.length;i++){
                    for(int j=0;j<(rr[0].length);j++){
                        tempMax = Math.max(tempMax,rr[i][j]);
                        //temp = Math.max(temp,Math.pow(tt[i][j],2));
                    }
                }
                //if(!(Math.sqrt(r.get(0, 0))>0 && Math.sqrt(r.get(0, 0))<1))
                //    this.myeeg.discoverylistener.log("***r= "+r.get(0,0)+" ***");
                result[index] = Math.sqrt(tempMax);

            }
        } catch (Exception e) {this.myeeg.discoverylistener.log(e);}
        return result;
    }*/
    /**
     * from 1Hz to 45Hz, 45+11=56
     */
    private void createSinCosDataBase(){
        for (float hz = 1; hz <57; hz += 0.25) {
            double[] s = new double[dataBasePoints];
            double[] c = new double[dataBasePoints];
            for (int i = 1; i < (dataBasePoints + 1); i++) {
                s[i - 1] = Math.sin(i * 2 * Math.PI * hz / sRate);
                c[i - 1] = Math.cos(i * 2 * Math.PI * hz / sRate);
            }
            SIN.addElement(s);
            COS.addElement(c);
        }
    }
    private void createSinCosDatabase2(){
        for (float hz = 9; hz < 13; hz += 0.25) {
            double[] s = new double[dataBasePoints];
            double[] c = new double[dataBasePoints];
            for (int i = 1; i < (dataBasePoints + 1); i++) {
                s[i - 1] = Math.sin(i * 2 * Math.PI * hz / sRate);
                c[i - 1] = Math.cos(i * 2 * Math.PI * hz / sRate);
            }
            SIN.addElement(s);
            COS.addElement(c);
        }
    }
    /**
     * ch1 ch2 ch3 ch4
     *  .   .   .   .
     *  .   .   .   .
     * @return double[][]
     */
    public double[][] readLineFromFile(String fileName){
        double[][] result = new double[tagOfPoints][4];
        StringBuffer tmp = new StringBuffer();
        String line;
        int length, idx1, indexOfLine, indexOfArray=0;
        byte[] buf;
        char[] buff = new char[30];
        InputStream is = getClass().getResourceAsStream(fileName);
        InputStreamReader stream = new InputStreamReader(is);
        try{
            while (is.available() > 0) {
                length = stream.read(buff, 0, buff.length);
                // append buffer to temp String
                if (length < buff.length) {
                    buf = new byte[length];
                    System.arraycopy(buff, 0, buf, 0, length);
                    tmp.append(new String(buf));
                } else {
                    tmp.append(new String(buff));
                }
                // look in tmp string for \r\n
                idx1 = tmp.toString().indexOf("\r\n");
                while (idx1 >= 0) {
                    //if found, split into line and rest of tmp
                    line = tmp.toString().substring(0, idx1);
                    int i=0;
                    while((indexOfLine = line.indexOf(" "))>0){
                        result[indexOfArray][i] = Double.parseDouble(line.substring(0,indexOfLine));
                        line = line.substring(indexOfLine+1);
                        i++;
                    }
                    indexOfArray++;
                    tmp = new StringBuffer(tmp.toString().substring(
                            idx1 + 2));
                    idx1 = tmp.toString().indexOf("\r\n");
                }
                if(indexOfArray>(tagOfPoints-1))
                    break;
            }
        }catch(Exception e){System.out.println(""+ e.toString());}
        return result;
    }
    private double getCov(Matrix aa,int c1, Matrix bb, int c2, int points){
        double [] a = new double[points];
        double [] b = new double[points];
        a = aa.getOneColumnArrayCopy(c1,points);
        b = bb.getOneColumnArrayCopy(c2,points);
        double result =0;
        double aMean = 0;
        double bMean = 0;
        double abSum = 0;
            for(int i=0;i<points;i++){
                aMean +=a[i];
                bMean +=b[i];
            }
            aMean /= points;
            bMean /= points;
            for(int i=0;i<points;i++){
                abSum +=(a[i]-aMean)*(b[i]-bMean);
            }
            result = abSum/(points-1);
            //result = Math.sqrt(abSum/a_length);
        return result;
    }
    private double getCov(double[] a, double[] b){
        double result =0;
        int a_length = a.length;
        int b_length = b.length;
        double aMean = 0;
        double bMean = 0;
        double abSum = 0;
        if(a_length == b_length){
            for(int i=0;i<a_length;i++){
                aMean +=a[i];
                bMean +=b[i];
            }
            aMean /= a_length;
            bMean /= b_length;
            for(int i=0;i<a_length;i++){
                abSum +=(a[i]-aMean)*(b[i]-bMean);
            }
            result = abSum/(a_length-1);
            //result = Math.sqrt(abSum/a_length);
        }
        else{
            System.out.println("Dimension is not match");
        }
        return result;
    }
    public double[][] getSinCosWave(float hz) {
        double[][] result = new double[dataBasePoints][2];
        for (int i = 1; i < (dataBasePoints+1); i++) {
            result[i - 1][0] = Math.sin(i * 2 * Math.PI * hz / sRate);
            result[i - 1][1] = Math.cos(i * 2 * Math.PI * hz / sRate);
        }
        return result;
    }
    private void ttRight() {
        double[][] EEG = readLineFromFile("1.txt");
        double[][] SinCos = getSinCosWave(11);
        double[][] z = new double[dataBasePoints][6];
        for (int i = 0; i < dataBasePoints; i++) {
            for (int j = 0; j < 4; j++) {
                z[i][j] = EEG[i][j];
            }
        }
        for (int i = 0; i < dataBasePoints; i++) {
            for (int j = 0; j < 2; j++) {
                z[i][j + 4] = SinCos[i][j];
            }
        }
        Matrix Z = new Matrix(z);
        double[][] c = new double[6][6];
        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < 6; j++) {
                c[i][j] = this.getCov(Z.getMatrix(0, (dataBasePoints-1), i, i).getColumnPackedCopy(),
                                      Z.getMatrix(0, (dataBasePoints-1), j, j).getColumnPackedCopy());
            }
        }
        Matrix C = new Matrix(c);
        Matrix Cxx = C.getMatrix(0, 3, 0, 3);
        Matrix Cxy = C.getMatrix(0, 3, 4, 5);
        Matrix Cyx = Cxy.transpose();
        Matrix Cyy = C.getMatrix(4, 5, 4, 5);
        Matrix invCyy = Cyy.inverse();
        //Matrix temp = Cxx.inverse().times(Cxy).times(invCyy).times(Cyx);
        EigenvalueDecomposition eig = new EigenvalueDecomposition(Cxx.inverse().
                times(Cxy).times(invCyy).times(Cyx));
        Matrix Wx = eig.getV();
        Matrix r = eig.getD();
        double a = Math.sqrt(r.get(0, 0));
        System.out.println(a);
        //this.append("HAHAHA: " +Double.toString(a));

    }
    private void tt(){
        double[][] EEG = this.readLineFromFile("1.txt");
        double [][] z = new double[dataBasePoints][6];
        for(int i=0;i<dataBasePoints;i++){
            for(int j=0;j<4;j++){
                z[i][j] = EEG[i][j];
            }
        }
        double []sin = (double[])this.SIN.elementAt(4);
        double []cos = (double[])this.COS.elementAt(4);

        for(int i=0;i<dataBasePoints;i++){
            z[i][4] = sin[i];
            z[i][5] = cos[i];
        }
        Matrix Z = new Matrix(z);
        double [][] c = new double[6][6];
        for(int i=0;i<6;i++){
            for(int j=0;j<6;j++){
                c[i][j] = this.getCov(Z.getMatrix(0,(dataBasePoints-1),i,i).getColumnPackedCopy(),
                          Z.getMatrix(0,(dataBasePoints-1),j,j).getColumnPackedCopy());
            }
        }
        Matrix C = new Matrix(c);
        Matrix Cxx = C.getMatrix(0,3,0,3);
        Matrix Cxy = C.getMatrix(0,3,4,5);
        Matrix Cyx = Cxy.transpose();
        Matrix Cyy = C.getMatrix(4,5,4,5);
        Matrix invCyy = Cyy.inverse();
        //Matrix temp = Cxx.inverse().times(Cxy).times(invCyy).times(Cyx);
        EigenvalueDecomposition eig = new EigenvalueDecomposition(Cxx.inverse().times(Cxy).times(invCyy).times(Cyx));
        Matrix Wx = eig.getV();
        Matrix r = eig.getD();
        double a= Math.sqrt(r.get(0,0));
        System.out.println(a);
    }

    public Vector getSin(){
        return this.SIN;
    }
    public Vector getCos(){
        return this.COS;
    }
}
