package main;

import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import java.util.Date;
import java.util.StringTokenizer;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;

public class EstimateARMAModelListener implements ActionListener {
    int new_p;
    int start_ARMA;
    int count;
    int q_value_ARMA;
    int used_b_row = 0;
    double suma_array[];
    double MA_array[];
    double MAE_;
    double MAE_best = 0;
    double all_errors[];
    double suma_errors;
    StartApplet applet;
    String data[][];
    String info_msg;
    private double[] used_data_ARMA;
    private JScrollPane data_table_ARMA_Scroll;
    private JTable coef_a_ARMA_Table, coef_b_ARMA_Table;
    private JScrollPane coef_a_table_ARMA_Scroll, coef_b_table_ARMA_Scroll;
    
    public EstimateARMAModelListener(StartApplet applet) {
        this.applet = applet;
    }
    
    public void actionPerformed(ActionEvent e) {
        start_ARMA = 0;
        info_msg = "";
        applet.real_ARMA_Series = new TimeSeries("Actual value", Day.class);

        int k = 0;
        if (!applet.your_data_Area.getText().equals("")) { //jeigu duomenis nuskaitom iš textArea lauko
            String line = applet.your_data_Area.getText();
            StringTokenizer t = new StringTokenizer(line, "\n");
            data = new String[applet.entered_data_Count][5];
            applet.entered_data_ARMA = new double[applet.entered_data_Count];
            while (t.hasMoreTokens()) {
                applet.entered_data_ARMA[k] = Double.parseDouble(t.nextToken());
                data[k][0] = Integer.toString(k+1) + ".";
                data[k][1] = Double.toString(applet.entered_data_ARMA[k]);
                Date now = new Date();
                Calendar cal = Calendar.getInstance();
                cal.setTime(now);
                cal.add(Calendar.DAY_OF_YEAR, k+1);
                Date tomorrow = cal.getTime();
                applet.real_ARMA_Series.add(new Day(tomorrow), applet.entered_data_ARMA[k]);
                k++;
            }
        }
        else { //jeigu duomenis nuskaitom iš lentelės
            String read_from = applet.read_Group.getSelection().getActionCommand();
            if ( applet.type.equals("5") ) applet.column_number = 1;
            else applet.column_number = Integer.parseInt(applet.column_Field.getText());
            data = new String[applet.entered_data_Count][5];
            applet.entered_data_ARMA = new double[applet.entered_data_Count];

            if (Integer.parseInt(read_from) == 1) { //jeigu duomenis nuskaitom iš viršaus į apačią
                for (int i = 0; i < applet.entered_data_Count; i++) {
                    applet.entered_data_ARMA[k] = Double.parseDouble(applet.table1.getValueAt(i, applet.column_number-1).toString());
                    data[k][0] = Integer.toString(k+1) + "."; //numeracija
                    data[k][1] = String.valueOf(applet.table1.getValueAt(i, applet.column_number-1));  //realūs duomenys
                    Date now = new Date();
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(now);
                    cal.add(Calendar.DAY_OF_YEAR, k+1);
                    Date tomorrow = cal.getTime();
                    applet.real_ARMA_Series.add(new Day(tomorrow), applet.entered_data_ARMA[k]);
                    k++;
                }
            }
            else { //jeigu duomenis nuskaitom iš apačios į viršų
                for (int i = applet.entered_data_Count-1; i >= 0; i--) {
                    applet.entered_data_ARMA[k] = Double.parseDouble(applet.table1.getValueAt(i, applet.column_number-1).toString());
                    data[k][0] = Integer.toString(k+1) + "."; //numeracija
                    data[k][1] = String.valueOf(applet.table1.getValueAt(i, applet.column_number-1)); //realūs duomenys
                    Date now = new Date();
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(now);
                    cal.add(Calendar.DAY_OF_YEAR, k+1);
                    Date tomorrow = cal.getTime();
                    applet.real_ARMA_Series.add(new Day(tomorrow), applet.entered_data_ARMA[k]);
                    k++;
                }
            }
        }

        String titles[] = new String[4];
        titles[0] = "No.";
        titles[1] = "Actual value";
        titles[2] = "Forecast";
        titles[3] = "Error";

        applet.p_value_ARMA = Integer.parseInt(applet.p_ARMA_Field.getText());
        q_value_ARMA = Integer.parseInt(applet.q_ARMA_Field.getText());

        double bendra_suma2 = 0;
        double bendra_suma3 = 0;
        double bendra_suma4 = 0;
        double bendra_suma5 = 0;
        applet.forecast_ARMA_Series = new TimeSeries("Forecast", Day.class);

        String virtual_data = applet.virtual_data_ARMA_Group.getSelection().getActionCommand();
        ARMA_method ar_ = null;
        double[] use_array;
        double b_from = Double.parseDouble(applet.b_from_ARMA_Field.getText().replace(",", "."));
        double b_to = Double.parseDouble(applet.b_to_ARMA_Field.getText().replace(",", "."));
        double step = Double.parseDouble(applet.b_step_ARMA_Field.getText().replace(",", "."));
        double diff = b_to - b_from;
        if ( diff < 0 ) diff = diff * (-1);
        int count_b = (int)(Math.round((diff/step)*1e9)/1e9)+1;
        int b_coefs_array_rows = applet.power(count_b, q_value_ARMA);
        double b_coefs_array[][] = new double[b_coefs_array_rows][q_value_ARMA];  
        double q[] = new double[q_value_ARMA];
        for ( int a = 0; a < q_value_ARMA; a++ ) q[a] = 0;
        boolean pere;
        int index = 0;
        
        if ( q_value_ARMA > 0 ) {
            for ( int j = 0; j < b_coefs_array_rows; j++ ) {
                for ( int a = 0; a < q_value_ARMA; a++ ) b_coefs_array[j][a] = b_to - step*q[a];
                index++;
                q[q_value_ARMA-1]++;
                pere = true;
                while( pere ) {
                    pere = false;
                    for ( int a = q_value_ARMA-1; a > 0; a-- ) {
                        if ( a - 1 < 0  ) {
                            pere = false;
                            break;
                        }
                        else if ( q[a] > count_b-1 ) {
                            q[a-1]++;
                            q[a] = 0;
                            pere = true;
                        }
                    }
                }
                if ( q[0] > count_b-1 ) break;
            }
        }
        
        System.out.println("---------b_coefs_array start-------------");
        for ( int i = 0; i < b_coefs_array_rows; i++ ) {
            for ( int j = 0; j < q_value_ARMA; j++ ) {
                System.out.print(applet.round(b_coefs_array[i][j],1)+ " " );
            }
            System.out.println();
        }
        System.out.println("---------b_coefs_array end-------------");
        
        if (virtual_data.equals("1")) { //jeigu naudojam atbulinius duomenis
            //start = 0;
            //double[] back_data_array = applet.back_data_array(applet.training_data_ARMA_Count+applet.p_value_ARMA, applet.training_data_ARMA_Count, entered_data_ARMA);
            //ar_ = new AR_method(back_data_array, applet.p_value_ARMA);
            //applet.coef_ARMA = ar_.rezult();
            //if ( applet.coef_ARMA.length < applet.p_value_ARMA ) info_msg = "No optimal solution\np value was reduced to " + applet.coef_ARMA.length;
        }
        else { //jeigu nenaudojam atbulinių duomenų
            if ( applet.p_value_ARMA >= q_value_ARMA ) start_ARMA = applet.p_value_ARMA;
            else start_ARMA = q_value_ARMA;
            use_array = new double[applet.training_data_ARMA_Count];
            System.arraycopy(applet.entered_data_ARMA, 0, use_array, 0, applet.training_data_ARMA_Count);
        }
        
        MAE_best = 0;
        for ( int b = 0; b < b_coefs_array_rows; b++ ) {
            all_errors = new double[applet.training_data_ARMA_Count];
            MA_array = new double[applet.training_data_ARMA_Count];
            MAE_ = 0;
            suma_errors = 0;
            count = 0;
            for (int i = start_ARMA; i < applet.training_data_ARMA_Count; i++) {
                used_data_ARMA = new double[i];
                System.arraycopy(applet.entered_data_ARMA, 0, used_data_ARMA, 0, i);
                if (used_data_ARMA.length < applet.p_value_ARMA*2) {
                    used_data_ARMA = applet.back_data_array(applet.p_value_ARMA*2, used_data_ARMA.length, applet.entered_data_ARMA);
                }
                
                ar_ = new ARMA_method(used_data_ARMA, applet.p_value_ARMA, MA_array, b_coefs_array[b]);
                applet.coef_ARMA = ar_.rezult();

                double suma = 0;
                for (int j = 0; j < applet.coef_ARMA.length; j++) {
                    suma += used_data_ARMA[used_data_ARMA.length-j-1] * applet.coef_ARMA[j];
                }
                if ( q_value_ARMA > 0 ) {
                    for (int h = 0; h < q_value_ARMA; h++) {
                        suma += b_coefs_array[b][h] * all_errors[i-h-1];
                    }
                }
                
                if (i < applet.entered_data_ARMA.length) {
                    double error = applet.entered_data_ARMA[i] - suma;
                    if (error < 0) error = error * (-1);
                    suma_errors += error;
                    all_errors[i] = error;
                    
                    for ( int b2 = 0; b2 < b_coefs_array[b].length; b2++ ) {
                        MA_array[i] += b_coefs_array[b][b2] * all_errors[i-1-b2];
                    }
                    count++;
                }
            }
            MAE_ = suma_errors / count;
            System.out.println(MAE_);
            if (MAE_best == 0 || MAE_ < MAE_best) {
                MAE_best = MAE_;
                used_b_row = b;
            }
        }
        
        all_errors = new double[applet.training_data_ARMA_Count];
        MA_array = new double[applet.training_data_ARMA_Count];
        MAE_ = 0;
        suma_errors = 0;
        count = 0;
        for (int i = start_ARMA; i < applet.training_data_ARMA_Count; i++) {
            used_data_ARMA = new double[i];
            System.arraycopy(applet.entered_data_ARMA, 0, used_data_ARMA, 0, i);
            if (used_data_ARMA.length < applet.p_value_ARMA*2) {
                used_data_ARMA = applet.back_data_array(applet.p_value_ARMA*2, used_data_ARMA.length, applet.entered_data_ARMA);
            }

            ar_ = new ARMA_method(used_data_ARMA, applet.p_value_ARMA, MA_array, b_coefs_array[used_b_row]);
            applet.coef_ARMA = ar_.rezult();

            double suma = 0;
            for (int j = 0; j < applet.coef_ARMA.length; j++) {
                suma += used_data_ARMA[used_data_ARMA.length-j-1] * applet.coef_ARMA[j];
            }
            if ( q_value_ARMA > 0 ) {
                for (int h = 0; h < q_value_ARMA; h++) {
                    suma += b_coefs_array[used_b_row][h] * all_errors[i-h-1];
                }
            }

            if (i < applet.entered_data_ARMA.length) {
                Date now = new Date();
                Calendar cal = Calendar.getInstance();
                cal.setTime(now);
                cal.add(Calendar.DAY_OF_YEAR, i+1);
                Date tomorrow = cal.getTime();
                applet.forecast_ARMA_Series.add(new Day(tomorrow), suma);

                double error = applet.entered_data_ARMA[i] - suma;
                bendra_suma5 += error;
                if (error < 0) error = error * (-1);
                suma_errors += error;
                all_errors[i] = error;
                
                //data[i][2] = Double.toString(applet.round(suma,3));
                //if (suma !== Double.NaN ) suma = applet.round(suma, 3);
                data[i][2] = Double.toString(suma);
                data[i][3] = Double.toString(error);
                bendra_suma2 += error / applet.entered_data_ARMA[i];
                bendra_suma3 += applet.entered_data_ARMA[i];
                bendra_suma4 += error * error;

                for ( int b2 = 0; b2 < b_coefs_array[used_b_row].length; b2++ ) {
                    MA_array[i] += b_coefs_array[used_b_row][b2] * all_errors[i-1-b2];
                }
                count++;
            }
        }
        
        /*all_errors = new double[applet.training_data_ARMA_Count];
        MA_array = new double[applet.training_data_ARMA_Count];
        MAE_ = 0;
        suma_errors = 0;
        count = 0;
        training_error_sum = 0;
        for (int i = start_ARMA; i < applet.training_data_ARMA_Count; i++) {
            used_data_ARMA = new double[i];
            System.arraycopy(applet.entered_data_ARMA, 0, used_data_ARMA, 0, i);
            if (used_data_ARMA.length < applet.p_value_ARMA*2) {
                used_data_ARMA = applet.back_data_array(applet.p_value_ARMA*2, used_data_ARMA.length, applet.entered_data_ARMA);
            }
            ar_ = new ARMA_method(used_data_ARMA, applet.p_value_ARMA, MA_array, b_coefs_array[used_b_row]);
            applet.coef_ARMA = ar_.rezult();

            double suma = 0;
            for (int j = 0; j < applet.coef_ARMA.length; j++) {
                suma += used_data_ARMA[used_data_ARMA.length-j-1] * applet.coef_ARMA[j];
            }
            if ( q_value_ARMA > 0 ) {
                for (int h = 0; h < q_value_ARMA; h++) {
                    suma += b_coefs_array[used_b_row][h] * all_errors[i-h-1];
                }
            }
            
            if (i < applet.training_data_ARMA_Count) {
                Date now = new Date();
                Calendar cal = Calendar.getInstance();
                cal.setTime(now);
                cal.add(Calendar.DAY_OF_YEAR, i+1);
                Date tomorrow = cal.getTime();
                applet.forecast_ARMA_Series.add(new Day(tomorrow), suma);
                double error = applet.entered_data_ARMA[i] - suma;
                bendra_suma5 += error;
                if (error < 0) error = error * (-1);
                training_error_sum += error;
                errors_ARMA[i] = error;
                data[i][2] = Double.toString(applet.round(suma,3));
                data[i][3] = Double.toString(applet.round(error,3));
                bendra_suma2 += error / applet.entered_data_ARMA[i];
                bendra_suma3 += applet.entered_data_ARMA[i];
                bendra_suma4 += error * error;
                count++;
            }
        }*/

        applet.data_mean_ARMA_Label.setVisible(true);
        applet.data_variance_ARMA_Label.setVisible(true);
        applet.MAE_ARMA_Label.setVisible(true);
        applet.MAPE_ARMA_Label.setVisible(true);
        applet.PMAD_ARMA_Label.setVisible(true);
        applet.MSE_ARMA_Label.setVisible(true);
        applet.RMSE_ARMA_Label.setVisible(true);
        applet.residual_variance_ARMA_Label.setVisible(true);
        applet.test_ARMA_Button.setVisible(true);
        applet.graphic_ARMA_Button.setVisible(true);
        applet.save_CSV_ARMA_Button.setVisible(true);
        applet.forecast_ARMA_Label.setVisible(true);
        applet.forecast_ARMA_Field.setVisible(true);

        if (applet.data_ARMA_Table != null) {
            data_table_ARMA_Scroll.remove(applet.data_ARMA_Table);
            applet.data_ARMA_Table = null;
        }
        applet.data_ARMA_Table = new JTable(data, titles) {
            @Override
            public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
                Component c = super.prepareRenderer(renderer, row, column);
                if (row < applet.training_data_ARMA_Count) c.setBackground(new Color(200,221,242));
                else c.setBackground(null);
                return c;
            }
        };
        if (data_table_ARMA_Scroll != null) {
            applet.ARMA_Panel.remove(data_table_ARMA_Scroll);
            data_table_ARMA_Scroll = null;
        }
        data_table_ARMA_Scroll = new JScrollPane(applet.data_ARMA_Table);
        data_table_ARMA_Scroll.setVerticalScrollBarPolicy(22);
        data_table_ARMA_Scroll.setBounds(10, 160, 400, 337);
        applet.ARMA_Panel.add(data_table_ARMA_Scroll);

        String titles2[] = new String[2];
        titles2[0] = "No.";
        titles2[1] = "a coefficient";

        String data2 [][] = new String[applet.p_value_ARMA][2];

        for (int i = 0; i < applet.coef_ARMA.length; i++) {
            data2[i][0] = Integer.toString(i+1) + ".";
            data2[i][1] = Double.toString(applet.coef_ARMA[i]);
        }
        if (applet.p_value_ARMA > applet.coef_ARMA.length) {
            for (int i = applet.coef_ARMA.length; i < applet.p_value_ARMA; i++) {
                data2[i][0] = Integer.toString(i+1) + ".";
                data2[i][1] = "0.0";
            }
        }

        if (coef_a_ARMA_Table != null) {
            coef_a_table_ARMA_Scroll.remove(coef_a_ARMA_Table);
            coef_a_ARMA_Table = null;
        }
        coef_a_ARMA_Table = new JTable(data2, titles2);
        if (coef_a_table_ARMA_Scroll != null) {
            applet.ARMA_Panel.remove(coef_a_table_ARMA_Scroll);
            coef_a_table_ARMA_Scroll = null;
        }
        coef_a_table_ARMA_Scroll = new JScrollPane(coef_a_ARMA_Table);
        coef_a_table_ARMA_Scroll.setVerticalScrollBarPolicy(22);
        coef_a_table_ARMA_Scroll.setBounds(430, 160, 200, 337);
        applet.ARMA_Panel.add(coef_a_table_ARMA_Scroll);    
        
        String titles3[] = new String[2];
        titles3[0] = "No.";
        titles3[1] = "b coefficient";

        String data3 [][] = new String[q_value_ARMA][2];

        for (int i = 0; i < q_value_ARMA; i++) {
            data3[i][0] = Integer.toString(i+1) + ".";
            data3[i][1] = Double.toString(b_coefs_array[used_b_row][i]);
        }

        if (coef_b_ARMA_Table != null) {
            coef_b_table_ARMA_Scroll.remove(coef_b_ARMA_Table);
            coef_b_ARMA_Table = null;
        }
        coef_b_ARMA_Table = new JTable(data3, titles3);
        if (coef_b_table_ARMA_Scroll != null) {
            applet.ARMA_Panel.remove(coef_b_table_ARMA_Scroll);
            coef_b_table_ARMA_Scroll = null;
        }
        coef_b_table_ARMA_Scroll = new JScrollPane(coef_b_ARMA_Table);
        coef_b_table_ARMA_Scroll.setVerticalScrollBarPolicy(22);
        coef_b_table_ARMA_Scroll.setBounds(660, 160, 200, 337);
        applet.ARMA_Panel.add(coef_b_table_ARMA_Scroll);
        
        double data_mean = 0;
        int c = 0;
        double training_data_sum = 0, training_data_sum2 = 0;
        for (int i = 0; i < applet.entered_data_ARMA.length; i++) {
            training_data_sum += applet.entered_data_ARMA[i];
            training_data_sum2 += applet.entered_data_ARMA[i]*applet.entered_data_ARMA[i];
            c++;
        }
        data_mean = training_data_sum / applet.entered_data_ARMA.length;
        applet.data_mean_value_ARMA_Label.setText(" = " + Double.toString(applet.round(data_mean, 3)));

        double data_variance = 0;
        data_variance = (training_data_sum2 - ((training_data_sum*training_data_sum)/c)) / (c - 1);
        applet.data_variance_value_ARMA_Label.setText(" = " + Double.toString(applet.round(data_variance, 3)));

        double MAE = 0;
        if (suma_errors == Double.NaN || suma_errors == Double.POSITIVE_INFINITY || suma_errors == Double.NEGATIVE_INFINITY ||
            suma_errors == 0.0)
            MAE = 0;
        else MAE = suma_errors / count;
        //applet.MAE_value_ARMA_Label.setText(" = " + Double.toString(applet.round(MAE, 3)));
        //if (MAE != Double.NaN ) MAE = applet.round(MAE, 3);
        applet.MAE_value_ARMA_Label.setText(" = " + Double.toString(MAE) );

        double MAPE = 0;
        if (bendra_suma2 == Double.NaN || bendra_suma2 == Double.POSITIVE_INFINITY || bendra_suma2 == Double.NEGATIVE_INFINITY ||
            suma_errors == 0.0)
            MAPE = 0;
        else MAPE = bendra_suma2 / count;
        applet.MAPE_value_ARMA_Label.setText(" = " + Double.toString(applet.round(MAPE, 3)));

        double PMAD = 0;
        if (suma_errors == Double.NaN || suma_errors == Double.POSITIVE_INFINITY || suma_errors == Double.NEGATIVE_INFINITY ||
            bendra_suma3 == Double.NaN || bendra_suma3 == Double.POSITIVE_INFINITY || bendra_suma3 == Double.NEGATIVE_INFINITY ||
            suma_errors == 0.0 || bendra_suma3 == 0.0)
                PMAD = 0;
        else PMAD = suma_errors / bendra_suma3;
        applet.PMAD_value_ARMA_Label.setText(" = " + Double.toString(applet.round(PMAD, 3)));

        double MSE = 0;
        if (bendra_suma4 == Double.NaN || bendra_suma4 == Double.POSITIVE_INFINITY || bendra_suma4 == Double.NEGATIVE_INFINITY ||
            bendra_suma4 == 0.0)
            MSE = 0;
        else MSE = bendra_suma4 / count;
        applet.MSE_value_ARMA_Label.setText(" = " + Double.toString(applet.round(MSE, 3)));

        double RMSE = 0;
        if (MSE == 0.0) RMSE = 0;
        else RMSE = Math.sqrt(MSE);
        applet.RMSE_value_ARMA_Label.setText(" = " + Double.toString(applet.round(RMSE, 3)));

        double residual_variance = 0;
        if ( count-1 > 0 ) residual_variance = (bendra_suma4 - ((bendra_suma5*bendra_suma5)/count)) / (count - 1);
        else residual_variance = 0;
        applet.residual_variance_value_ARMA_Label.setText(" = " + Double.toString(applet.round(residual_variance, 3)));

        //if ( !info_msg.isEmpty() ) JOptionPane.showMessageDialog(applet.ARMA_Panel, info_msg, "Information", JOptionPane.INFORMATION_MESSAGE);
    }   
}