/**
 * Copyright (c) Artenum SARL 2004-2005
 * @author Julien Forest / Sebastien Jourdain
 *
 * All rights reserved. This software can
 * not be used or copy or diffused without
 * an explicit license of Artenum SARL, Paris-France
 */
package com.artenum.cassandra.plugin.defaults;

import vtk.vtkDataArray;
import vtk.vtkDataSet;
import vtk.vtkFloatArray;
import vtk.vtkIdList;
import vtk.vtkPoints;
import vtk.vtkUnstructuredGrid;

import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * @author Sebastien
 */
public class Line3dWithData {
    private ArrayList pointList;
    private ArrayList dataList;
    private vtkUnstructuredGrid grid;
    private int[] nbParamByData;

    public Line3dWithData(int[] nbParamByData) {
        pointList = new ArrayList();
        dataList = new ArrayList();
        grid = null;
        this.nbParamByData = nbParamByData;
    }

    public void load(String filePath) throws IOException {
        FileReader fr = new FileReader(filePath);
        LineNumberReader lnr = new LineNumberReader(fr);
        String line = null;
        String[] split = null;
        double[] point = new double[3];
        int currentIndex;
        DataObject data = null;
        while ((line = lnr.readLine()) != null) {
            split = line.split(" ");
            currentIndex = 0;
            System.out.println("A");
            data = new DataObject(nbParamByData);
            System.out.println("B");
            for (int i = 0; i < split.length; i++) {
                System.out.println("C");
                if (split[i].trim().length() > 0) {
                    if (currentIndex < point.length) {
                        // Point coord
                        System.out.println("Set point: " + split[i]);
                        point[currentIndex++] = Double.parseDouble(split[i]);
                    } else {
                        // Point data
                        System.out.println("Set data: " + split[i]);
                        data.addData(Double.parseDouble(split[i]));
                    }
                }
            }

            addPoint(point[0], point[1], point[2]);
            addData(data);
        }

        lnr.close();
    }

    public void addPoint(double x, double y, double z) {
        pointList.add(new double[] { x, y, z });
    }

    public void addData(DataObject data) {
        dataList.add(data);
    }

    public void reset() {
        pointList.clear();
        grid = null;
    }

    private void buildVtkDataSet() {
        double[] point = null;
        vtkIdList idList = null;
        int id = 0;
        grid = new vtkUnstructuredGrid();
        grid.Allocate(pointList.size() - 1, pointList.size());
        vtkPoints points = new vtkPoints();

        //
        vtkFloatArray pcoords = new vtkFloatArray();
        pcoords.SetNumberOfComponents(3);
        pcoords.SetNumberOfTuples(pointList.size());
        for (Iterator i = pointList.iterator(); i.hasNext();) {
            points.InsertPoint(id++, (double[]) i.next());
        }

        grid.SetPoints(points);

        // Set edge
        for (int i = 0; i < (id - 1); i++) {
            idList = new vtkIdList();
            idList.InsertNextId(i);
            idList.InsertNextId(i + 1);
            grid.InsertNextCell(3, idList);
        }

        // Manage data
        vtkDataArray currentVtkData = null;
        double[] data;
        int currentNodeId;
        System.out.println("Param " + nbParamByData.length + " / " + (dataList.size()));
        for (int i = 0; i < nbParamByData.length; i++) {
            System.out.println("i: " + i);
            currentVtkData = new vtkDataArray();
            currentVtkData.SetNumberOfComponents(dataList.size());
            currentVtkData.SetNumberOfTuples(nbParamByData[i] * dataList.size());
            currentNodeId = 0;
            for (int j = i; j < dataList.size(); j = j + i) {
                System.out.println("j: " + j);
                data = ((DataObject) dataList.get(j)).getDataAt(i);
                System.out.println("nodeID: " + currentNodeId);
                switch (data.length) {
                case 1:
                    currentVtkData.InsertTuple1(currentNodeId++, data[0]);
                    break;
                case 2:
                    currentVtkData.InsertTuple2(currentNodeId++, data[0], data[1]);
                    break;
                case 3:
                    currentVtkData.InsertTuple3(currentNodeId++, data[0], data[1], data[2]);
                    break;
                case 4:
                    currentVtkData.InsertTuple4(currentNodeId++, data[0], data[1], data[2], data[3]);
                    break;
                case 9:
                    currentVtkData.InsertTuple9(currentNodeId++, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8]);
                    break;
                default:
                    throw new RuntimeException("DataSize on node of " + data.length + " data is not supported.");
                }
            }

            System.out.println("AddArray");
            System.out.flush();
            grid.GetPointData().AddArray(currentVtkData);
        }

        grid.Update();
    }

    public vtkDataSet getDataSet() {
        if (grid == null) {
            buildVtkDataSet();
        }

        return grid;
    }

    public class DataObject {
        private ArrayList data;
        private double[] currentData;
        private int currentDataIndex;
        private int currentIndex;

        public DataObject(int[] nbParamByData) {
            System.out.println("A a");
            data = new ArrayList();
            currentDataIndex = 0;
            currentIndex = 0;
            for (int i = 0; i < nbParamByData.length; i++) {
                System.out.println("A b");
                currentData = new double[nbParamByData[i]];
                data.add(currentData);
            }

            System.out.println("A c");
            currentData = getDataAt(currentIndex++);
            System.out.println("A d");
        }

        public void addData(double value) {
            if (currentIndex == currentData.length) {
                // change data
                currentIndex = 0;
                currentData = getDataAt(currentDataIndex++);
            }

            currentData[currentIndex++] = value;
        }

        public int getNbData() {
            return data.size();
        }

        public double[] getDataAt(int index) {
            return (double[]) data.get(index);
        }
    }
}
