package org.fda.graphdrawing.chart;

import java.awt.BasicStroke;
import java.awt.Color;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import org.fda.data.Utilities;
import org.fda.exceptions.ImageFileFormatNotFoundException;
import org.fda.graphdrawing.ColorList;
import org.fda.graphdrawing.chart.TwoDPlot;
import org.fda.graphdrawing.data.CategoryLineChartDataset;
import org.fda.graphdrawing.data.CategoryLineChartDatasetSeries;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLabelLocation;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.block.BlockBorder;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.DefaultDrawingSupplier;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.RectangleEdge;

/**
 *
 * @author Linlin.Zhao
 */

public class TwoDCategoryLinePlot extends TwoDPlot  {    
    private final CategoryLineChartDataset dataList;
    private int xAxisLabelRotation =0;
    private float lineWidth=2.0f;

    // Constructor with default chart parameters
    public TwoDCategoryLinePlot(CategoryLineChartDataset dataseries, File file) throws ImageFileFormatNotFoundException{
        dataList = dataseries;       
        this.file = file;
        if(!setFormat())
            throw new ImageFileFormatNotFoundException();
        
        List<Double> tmpY = new ArrayList<Double>();
        for(CategoryLineChartDatasetSeries ld:dataList.getData()){
            tmpY.add(ld.getYMax());
        }
        int y = (int)Math.ceil(Collections.max(tmpY));
        String ys = y+"";
        String ylower = "1";
        String ymiddle = "2";
        String yupper ="5";
        String yhighest = "10";
        if(ys.length()>1){
            for(int i=0; i<ys.length()-1; i++){
                ymiddle = ymiddle+"0";
                ylower = ylower+"0";
                yupper = yupper+"0";
                yhighest = yhighest +"0";
            }
            if(Collections.max(tmpY)>=Integer.valueOf(ylower)&&Collections.max(tmpY)<Integer.valueOf(ymiddle)){
                this.yAxisTickUnit = Integer.valueOf(ymiddle)/10;
            }
            else if(Collections.max(tmpY)>=Integer.valueOf(ymiddle)&&Collections.max(tmpY)<Integer.valueOf(yupper)){
                this.yAxisTickUnit = Integer.valueOf(yupper)/10;
            }
            else if(Collections.max(tmpY)>=Integer.valueOf(yupper)){
                this.yAxisTickUnit = Integer.valueOf(yhighest)/10;
            }
        }
        else{
            if(Collections.max(tmpY)<=1){
                this.yAxisTickUnit = 0;
            }
            else if(Collections.max(tmpY)>1&&Collections.max(tmpY)<=2){
                this.yAxisTickUnit =0.2;
            }
            else if(Collections.max(tmpY)>2&&Collections.max(tmpY)<=5){
                this.yAxisTickUnit =0.5;
            }
            else if(Collections.max(tmpY)>5&&Collections.max(tmpY)<10){
                this.yAxisTickUnit = 1.0;
            }
        }
    }

    // Constructor with detailed chart parameters
    public TwoDCategoryLinePlot(CategoryLineChartDataset dataseries, File file, String chartName,
            int chartWidth, int chartHeight, String xName, String yName, Color plotBGColor,Color gridlineColor, boolean legend) throws ImageFileFormatNotFoundException{
        this.file = file;
        if(!setFormat())
            throw new ImageFileFormatNotFoundException();
        dataList = dataseries;
        
        this.chartName = chartName;
        this.legendIsVisible = legend;
        this.width = chartWidth;
        this.height = chartHeight;
        this.xAxisName = xName;
        this.yAxisName = yName;
        this.plotBackgroundPaintColor = plotBGColor;
        this.horizontalGridlineColor= gridlineColor;
        this.verticalGridlineColor =gridlineColor;

        this.xAxisLabelLocation = AxisLabelLocation.HIGH_END;
        this.yAxisLabelLocation = AxisLabelLocation.HIGH_END;
        this.legendLocation = RectangleEdge.RIGHT;

        List<Double> tmpY = new ArrayList<Double>();
        for(CategoryLineChartDatasetSeries ld:dataList.getData()){
            tmpY.add(ld.getYMax());
        }
        int y = (int)Math.ceil(Collections.max(tmpY));
        String ys = y+"";
        String ylower = "1";
        String ymiddle = "2";
        String yupper ="5";
        String yhighest = "10";
        if(ys.length()>1){
            for(int i=0; i<ys.length()-1; i++){
                ymiddle = ymiddle+"0";
                ylower = ylower+"0";
                yupper = yupper+"0";
                yhighest = yhighest +"0";
            }
            if(Collections.max(tmpY)>=Integer.valueOf(ylower)&&Collections.max(tmpY)<Integer.valueOf(ymiddle)){
                this.yAxisTickUnit = Integer.valueOf(ymiddle)/10;
            }
            else if(Collections.max(tmpY)>=Integer.valueOf(ymiddle)&&Collections.max(tmpY)<Integer.valueOf(yupper)){
                this.yAxisTickUnit = Integer.valueOf(yupper)/10;
            }
            else if(Collections.max(tmpY)>=Integer.valueOf(yupper)){
                this.yAxisTickUnit = Integer.valueOf(yhighest)/10;
            }
        }
        else{
            if(Collections.max(tmpY)<=1){
                this.yAxisTickUnit = 0;
            }
            else if(Collections.max(tmpY)>1&&Collections.max(tmpY)<=2){
                this.yAxisTickUnit =0.2;
            }
            else if(Collections.max(tmpY)>2&&Collections.max(tmpY)<=5){
                this.yAxisTickUnit =0.5;
            }
            else if(Collections.max(tmpY)>5&&Collections.max(tmpY)<10){
                this.yAxisTickUnit = 1.0;
            }
        }
    }
    
    
    private CategoryDataset createDataset(List<CategoryLineChartDatasetSeries> dataList){
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        for(CategoryLineChartDatasetSeries cd:dataList){
            for(int i=0; i<cd.getSize();i++){
                dataset.addValue(cd.getYlist().get(i), cd.getSerieName(), cd.getXlist().get(i));
            }
        }
        return dataset;
    }
    
    private JFreeChart createChart(CategoryDataset dataset) {
        
        JFreeChart chart = ChartFactory.createLineChart(
                this.chartName,
                this.xAxisName,                  // domain axis label
                this.yAxisName,                     // range axis label
                dataset,                     // data
                PlotOrientation.VERTICAL,    // the plot orientation
                legendIsVisible,                        // legend
                true,                        // tooltips
                false                        // urls
        );
        
        //set chart background, border, title, legend(location, visible, frame)
        chart.setBackgroundPaint(this.chartBackgroundPaintColor);
        
        chart.setBorderPaint(this.chartOutlineColor);
        chart.setBorderVisible(this.chartOutlineIsVisible);
        
        chart.getTitle().setVisible(this.chartNameIsVisible);
        chart.getTitle().setFont(this.chartNameFont);
        
        chart.getLegend().setPosition(this.legendLocation);
        chart.getLegend().setItemFont(this.legendNameFont);
        if (this.legendFrameIsVisible != true){
            chart.getLegend().setFrame(BlockBorder.NONE);
        }
        
        for(TextTitle t : this.legendText){
            chart.addSubtitle(t);
        }
        
        //set plot background, outline, gridline
        CategoryPlot plot = chart.getCategoryPlot();
        plot.setBackgroundPaint(this.plotBackgroundPaintColor);
        
        plot.setOutlinePaint(this.plotOutlineColor);
        plot.setOutlineVisible(this.plotOutlineIsVisible);
        
        plot.setRangeGridlinePaint(this.horizontalGridlineColor);
        plot.setRangeGridlinesVisible(this.horizontalGridlineIsVisible);
        
        plot.setDomainGridlinePaint(this.verticalGridlineColor);
        plot.setDomainGridlinesVisible(this.verticalGridlineIsVisible);
    
        //set y axis (nameFont,nameLocation,tickLabelFont,color,range,unit,visible,numberFormat)
        NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
        
        rangeAxis.setLabelFont(this.yAxisNameFont);
        rangeAxis.setLabelLocation(this.yAxisLabelLocation);
        
        if(this.yAxisTickLabelIsVisible){
            rangeAxis.setTickLabelPaint(this.yAxisTickLabelColor);
            rangeAxis.setTickLabelFont(this.yAxisTickMarkFont);
        }
        else{
            rangeAxis.setTickLabelPaint(this.plotBackgroundPaintColor);
        }
        
        rangeAxis.setAxisLineVisible(yAxisIsVisible);
        if(this.yAxisIsVisible){
            rangeAxis.setAxisLinePaint(this.yAxisColor);
            rangeAxis.setTickMarkPaint(this.yAxisColor);
        }
        else{
            rangeAxis.setTickMarkPaint(this.plotBackgroundPaintColor);
        }
        if (this.yAxisLowerBound !=0 ){
            rangeAxis.setLowerBound(this.yAxisLowerBound);
        }
        if (this.yAxisUpperBound !=0 ){
            rangeAxis.setUpperBound(this.yAxisUpperBound);
        }
        
        if (this.yAxisTickUnit > 0){
            rangeAxis.setTickUnit(new NumberTickUnit(this.yAxisTickUnit));
        }
        
        if (this.yAxisNumFormat !=null){
            DecimalFormat df = new DecimalFormat(this.yAxisNumFormat);
            rangeAxis.setNumberFormatOverride(df);
        }
        rangeAxis.setUpperMargin(this.upperMargin);
        rangeAxis.setLowerMargin(this.lowerMargin);

        
        plot.setRangeAxis(rangeAxis);
        
        //set x axis (nameFont,nameLocation,tickLabelFont,color,range,unit)
        CategoryAxis domainAxis = plot.getDomainAxis();
        
        domainAxis.setLabelFont(this.xAxisNameFont);
        domainAxis.setLabelLocation(this.xAxisLabelLocation);
        
        if(this.xAxisTickLabelIsVisible){
            domainAxis.setTickLabelPaint(this.xAxisTickLabelColor);
            domainAxis.setTickLabelFont(this.xAxisTickMarkFont);
        }
        else{
            domainAxis.setTickLabelPaint(this.plotBackgroundPaintColor);
        }
        
        if(this.xAxisIsVisible){
            domainAxis.setAxisLinePaint(this.xAxisColor);
            domainAxis.setTickMarkPaint(this.xAxisColor);
        }
        else{
            domainAxis.setTickMarkPaint(this.plotBackgroundPaintColor);
        }
        
        domainAxis.setAxisLineVisible(xAxisIsVisible);
        
        switch (this.xAxisLabelRotation){
            case 0: break;
            case 1: {
                domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45);
                break;
            }
            case 2: {
                domainAxis.setCategoryLabelPositions(CategoryLabelPositions.createUpRotationLabelPositions(Math.PI/3.0));
                break;
            }
            case 3: {
                domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90);
                break;
            }
            
        }
        
        plot.setDomainAxis(domainAxis);
        
        //set renderer(base shape, color, line)
        if(this.defaultColors!= true){
        ColorList color = new ColorList();
        chart.getPlot().setDrawingSupplier(new DefaultDrawingSupplier(color.getColors(),
                DefaultDrawingSupplier.DEFAULT_OUTLINE_PAINT_SEQUENCE,
                DefaultDrawingSupplier.DEFAULT_STROKE_SEQUENCE,
                DefaultDrawingSupplier.DEFAULT_OUTLINE_STROKE_SEQUENCE,
                DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE)
        );
        }
        LineAndShapeRenderer renderer = new LineAndShapeRenderer();
        for(int i=0; i<this.dataList.getData().size(); i++){
            renderer.setLegendTextFont(i, this.legendNameFont);
            renderer.setSeriesStroke(i,new BasicStroke(this.lineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,1.0f, new float[] {6.0f, 6.0f}, 0.0f));
        }
        plot.setRenderer(renderer);
        
        return chart;
    }
    
     public boolean draw(){
        CategoryDataset data = createDataset(this.dataList.getData());
        JFreeChart chart = createChart(data);
        saveAsFile(chart);
        return true;
    }
    protected void saveAsFile(JFreeChart chart){
        super.saveAsFile(chart);
                
        // Now do the extra work of saving the data in the table format
        try(BufferedWriter bw = new BufferedWriter(new FileWriter(new File(this.file.getAbsolutePath().replaceAll(Utilities.format.getExtension(), ".txt"))))){
            bw.append(this.dataList.toTable());
        }
        catch(IOException io){}
    }

    public void setXAxisLabelRotation(int xr){
        try{
            this.xAxisLabelRotation = xr;
        }catch(Exception e){
        }
    }
    
    public void setLineWidth(float lw){
        try{
            this.lineWidth = lw;
        }catch(Exception e){
        }
    }
    
    public static void main(String[] args) {
        int row = 4;
        int col = 6;
        List<String> colName = new ArrayList<String>();
        List<String> rowName = new ArrayList<String>();
        for(int i=0;i<col;i++){
            colName.add("Col"+i);
        }
        
        Random r = new Random();
        for(int j=0; j<row; j++){
            rowName.add("Row"+j);
        }
        CategoryLineChartDataset dataseries = new CategoryLineChartDataset();
        //List<CategoryLineChartDatasetSeries> cdl = new ArrayList<CategoryLineChartDatasetSeries>();
        for(int k=0; k<row; k++){
            List<Double> rowN = new ArrayList<Double>();
            for(int m=0;m<col;m++){
                rowN.add(10.0 +(100.0 - 10.0)*r.nextDouble());
            }
            CategoryLineChartDatasetSeries cd = new CategoryLineChartDatasetSeries(rowName.get(k),colName,rowN);
            //cdl.add(cd);
            dataseries.addSeries(cd);
            
        }
        System.out.println(dataseries.toTable());
        try{
            TwoDCategoryLinePlot tmp2 = new TwoDCategoryLinePlot(dataseries, new File("C:\\Users\\Gokhan.Yavas\\Documents\\tmp2.pdf"));
            tmp2.draw();
        }
        catch(Exception e){}
    }
}
