import java.util.HashMap;
import java.util.List;
import processing.core.*;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import com.vividsolutions.jump.geom.*;
import com.vividsolutions.jts.geom.*;
import org.geotools.shapefile.*;
import processing.core.PApplet;
import com.vividsolutions.jump.io.*;
import com.vividsolutions.jump.feature.*;
import com.vividsolutions.jump.workbench.ui.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
import org.jgrapht.*;
import org.jgrapht.alg.DijkstraShortestPath;
import org.jgrapht.graph.*;
import codeanticode.glgraphics.*;
import processing.opengl.*;
import javax.media.opengl.GL;
import java.util.HashMap;
import java.util.Map;

public class SloRoads {
  private PApplet p;
  private Shapefile shp;
  private List<Feature> features;
  private float minx, miny, maxx, maxy, mwidth, mheight;
  private float panx, pany, cenx, ceny;
  private float scalex, scaley, mapscale, zoomfactor, mapoffsetx, mapoffsety;
  private float mpointx, mpointy, jpointx, jpointy;
  private FeatureCollection fc;

  private SimpleGraph<String, RoadEdge> net; // DefaultDirectedGraph
  private List<RoadVertex> crossings, beginnings, endings;
  private List<RoadEdge> roads;
  private Map<Integer, List<RoadVertex>> segments = new HashMap <Integer, List <RoadVertex>>();
  GLModel lines;

  SloRoads(PApplet parent) {
    p = parent;
    roads = new ArrayList<RoadEdge>();
    crossings = new ArrayList<RoadVertex>();
    beginnings = new ArrayList<RoadVertex>();
    endings = new ArrayList<RoadVertex>();
    net = new SimpleGraph<String, RoadEdge>(
    new ClassBasedEdgeFactory<String, RoadEdge>(RoadEdge.class)); // DefaultEdge.class
    Random r = new Random();

    

    drawShapeFromJSON();
  }

  // LZ: 454457.61021001264 105142.32072543819
  // DZ: 470965.3089227928 105041.24454015028
  // LS: 454391.38283338863 96264.97118422389
  // DS: 470922.89745327923 96163.88513510302


  public void drawShapeFromJSON() {
    JSONParser parser = new JSONParser();
    int i, j;
    i = j = 0;

    try {
      //Object in = parser.parse((new FileReader("C:\\Users\\solipsy\\Documents\\Processing\\!MAPS\\vector_gl\\Data\\paths.json")));
      Object in = parser.parse((new FileReader(p.dataPath("paths.json"))));
      JSONObject jsonObject = (JSONObject) in;
      JSONArray paths = (JSONArray) jsonObject.get(("paths"));
      Iterator<JSONArray> i1 = paths.iterator();
      while (i1.hasNext ()) { // seznam

        JSONArray path = (JSONArray) i1.next();
        int loopLength = path.size();
        j = 0;
        Iterator<JSONArray> i2 = path.iterator();
        while (i2.hasNext ()) { // poti
          JSONArray coords = (JSONArray) i2.next();
          Double x = (Double) coords.get(0);
          Double y = (Double) coords.get(1);
          double x1 = x;
          double y1 = y;
          float jpointx = (float) x1; // (float) coords.get(0);
          float jpointy = (float) y1;
          //p.println (jpointx + " " + jpointy);
          // add vertices
          String vertexID = Integer.toString(i) + "+"
            + Integer.toString(j);
          net.addVertex(vertexID);
          crossings.add(new RoadVertex(p, jpointx, jpointy, vertexID));

          // beginnings
          if (j == 0) {
            vertexID = Integer.toString(i) + "+"+ Integer.toString(j);
            beginnings.add(new RoadVertex(p, jpointx, jpointy, vertexID));
          }

          // endings
          if (j == loopLength - 1) {
            vertexID = Integer.toString(i) + "+"+ Integer.toString(j);
            endings.add(new RoadVertex(p, jpointx, jpointy, vertexID));
          }

          // add roads
          if (j > 0
            && crossings.size() > 1
            && !net.containsEdge(
          crossings.get(crossings.size() - 2).getId(), 
          crossings.get(crossings.size() - 1).getId())) {
            RoadEdge road = new RoadEdge(p, crossings.get(crossings
              .size() - 2), 
            crossings.get(crossings.size() - 1));
            net.addEdge(
            crossings.get(crossings.size() - 2).getId(), 
            crossings.get(crossings.size() - 1).getId(), 
            road);
            roads.add(road);
          }
          j++;
        }
        i++;
      }
      // PApplet.println("----------");
    } 
    catch (FileNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } 
    catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } 
    catch (ParseException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    for (i = 0; i < endings.size(); i++) {
      for (j = 0; j < beginnings.size(); j++) {
        if (endings.get(i).getX() == beginnings.get(j).getX()
          && endings.get(i).getY() == beginnings.get(j).getY()
          && !net.containsEdge(endings.get(i).getId(), beginnings
          .get(j).getId())) {
          RoadEdge road = new RoadEdge(p, endings.get(i), 
          beginnings.get(j));
          net.addEdge(endings.get(i).getId(), beginnings.get(j)
            .getId(), road);
        }
      }
    }

    for (i = 0; i < endings.size(); i++) {
      for (j = 1; j < endings.size(); j++) {
        if (i != j
          && endings.get(i).getX() == endings.get(j).getX()
          && endings.get(i).getY() == endings.get(j).getY()
          && !net.containsEdge(endings.get(i).getId(), endings
          .get(j).getId())) {
          RoadEdge road = new RoadEdge(p, endings.get(i), 
          endings.get(j));
          net.addEdge(endings.get(i).getId(), endings.get(j).getId(), 
          road);
        }
      }
    }

    for (i = 0; i < beginnings.size(); i++) {
      for (j = 1; j < beginnings.size(); j++) {
        if (i != j
          && beginnings.get(i).getX() == beginnings.get(j).getX()
          && beginnings.get(i).getY() == beginnings.get(j).getY()
          && !net.containsEdge(beginnings.get(i).getId(), 
        beginnings.get(j).getId())) {
          RoadEdge road = new RoadEdge(p, beginnings.get(i), 
          beginnings.get(j));
          net.addEdge(beginnings.get(i).getId(), beginnings.get(j)
            .getId(), road);
        }
      }
    }

    // segments
    for (RoadVertex cross : crossings) {
      if (!segments.containsKey(cross.getSegment())) {
        List<RoadVertex> listseg = new ArrayList<RoadVertex>();
        listseg.add(cross);
        segments.put(cross.getSegment(), listseg);
      } 
      else {
        List<RoadVertex> listseg = segments.get(cross.getSegment());
        listseg.add(cross);
      }
    }

    //GL model
    lines = new GLModel(p, roads.size() * 2 + 3, p.LINES, GLModel.STATIC);
    lines.initColors();

    lines.beginUpdateVertices();
    int counter = 0;
    for (i = 0; i < roads.size(); i+=1) {
      lines.updateVertex(counter, roads.get(i).getFrom().getX(), roads.get(i).getFrom().getY(), 0);
      lines.updateVertex(counter+1, roads.get(i).getTo().getX(), roads.get(i).getTo().getY(), 0);
      counter+=2;
    }
    lines.endUpdateVertices();

    lines.beginUpdateColors();
    counter = 0;
    for (i = 0; i < roads.size(); i+=1) {
      lines.updateColor(counter, 255, 255, 255, 255);
      lines.updateColor(counter+1, 255, 255, 255, 255);
      counter+=2;
    }
    lines.endUpdateColors();    
    
  }

  void drawShapeFile() {
    Random r = new Random();
    int c = 0;
    for (int i = 0; i < features.size(); i++) // features.size()
    {
      float mpointxOne = (float) features.get(i).getGeometry()
        .getCoordinates()[1].x;
      float mpointyOne = (float) features.get(i).getGeometry()
        .getCoordinates()[1].y;
      if  (mpointxOne > 457457 && mpointxOne < 468000 && mpointyOne > 96000 && mpointyOne < 105000) {

        c++;
        int loopLength = features.get(i).getGeometry().getCoordinates().length;
        // p.println (loopLength);
        // p.stroke(r.nextInt(255), r.nextInt(255), r.nextInt(255));
        for (int j = 0; j < loopLength; j++) //
        {
          mpointx = (float) features.get(i).getGeometry()
            .getCoordinates()[j].x;
          mpointy = (float) features.get(i).getGeometry()
            .getCoordinates()[j].y;

          if (j == 0) {
            mpointxOne = (mpointxOne - minx) / (float) mapscale;
            mpointyOne = (mpointyOne - miny) / (float) mapscale;
            mpointyOne = p.height - mpointyOne;
          }
          jpointx = (mpointx - minx) / (float) mapscale;
          jpointy = (mpointy - miny) / (float) mapscale;
          jpointy = p.height - jpointy;

          // add vertices
          String vertexID = Integer.toString(i) + "+"
            + Integer.toString(j);
          net.addVertex(vertexID);
          crossings
            .add(new RoadVertex(p, jpointx, jpointy, vertexID));

          // beginnings
          if (j == 0) {
            vertexID = Integer.toString(i) + "+"
              + Integer.toString(j);
            beginnings.add(new RoadVertex(p, jpointx, jpointy, 
            vertexID));
          }

          // endings
          if (j == loopLength - 1) {
            vertexID = Integer.toString(i) + "+"
              + Integer.toString(j);
            endings.add(new RoadVertex(p, jpointx, jpointy, 
            vertexID));
          }

          // add roads
          if (j > 0
            && crossings.size() > 1
            && !net.containsEdge(
          crossings.get(crossings.size() - 2).getId(), 
          crossings.get(crossings.size() - 1).getId())) {
            RoadEdge road = new RoadEdge(p, crossings.get(crossings
              .size() - 2), 
            crossings.get(crossings.size() - 1));
            net.addEdge(
            crossings.get(crossings.size() - 2).getId(), 
            crossings.get(crossings.size() - 1).getId(), 
            road);
            roads.add(road);
          }

          mpointxOne = jpointx;
          mpointyOne = jpointy;
        }
      }
    }
    // fix beginnings & endings
    for (int i = 0; i < endings.size(); i++) {
      for (int j = 0; j < beginnings.size(); j++) {
        if (endings.get(i).getX() == beginnings.get(j).getX()
          && endings.get(i).getY() == beginnings.get(j).getY()
          && !net.containsEdge(endings.get(i).getId(), beginnings
          .get(j).getId())) {
          RoadEdge road = new RoadEdge(p, endings.get(i), beginnings.get(j));
          net.addEdge(endings.get(i).getId(), beginnings.get(j)
            .getId(), road);
        }
      }
    }

    for (int i = 0; i < endings.size(); i++) {
      for (int j = 1; j < endings.size(); j++) {
        if (i != j
          && endings.get(i).getX() == endings.get(j).getX()
          && endings.get(i).getY() == endings.get(j).getY()
          && !net.containsEdge(endings.get(i).getId(), endings
          .get(j).getId())) {
          RoadEdge road = new RoadEdge(p, endings.get(i), endings.get(j));
          net.addEdge(endings.get(i).getId(), endings.get(j).getId(), 
          road);
        }
      }
    }

    for (int i = 0; i < beginnings.size(); i++) {
      for (int j = 1; j < beginnings.size(); j++) {
        if (i != j
          && beginnings.get(i).getX() == beginnings.get(j).getX()
          && beginnings.get(i).getY() == beginnings.get(j).getY()
          && !net.containsEdge(beginnings.get(i).getId(), 
        beginnings.get(j).getId())) {
          RoadEdge road = new RoadEdge(p, beginnings.get(i), beginnings.get(j));
          net.addEdge(beginnings.get(i).getId(), beginnings.get(j)
            .getId(), road);
        }
      }
    }

    for (RoadVertex cross : crossings) {
      if (!segments.containsKey(cross.getSegment())) {
        List <RoadVertex> listseg = new ArrayList <RoadVertex>();  
        listseg.add(cross);
        segments.put(cross.getSegment(), listseg);
      }
      else {
        List <RoadVertex> listseg = segments.get(cross.getSegment());
        listseg.add(cross);
      }
    }

    p.println ("-> model ok");
    // create gl model
    lines = new GLModel(p, roads.size() * 2 + 3, p.LINES, GLModel.STATIC);
    lines.initColors();

    lines.beginUpdateVertices();
    int counter = 0;
    for (int i = 0; i < roads.size(); i+=1) {
      lines.updateVertex(counter, roads.get(i).getFrom().getX(), roads.get(i).getFrom().getY(), 0);
      lines.updateVertex(counter+1, roads.get(i).getTo().getX(), roads.get(i).getTo().getY(), 0);
      counter+=2;
    }
    lines.endUpdateVertices();

    lines.beginUpdateColors();
    counter = 0;
    for (int i = 0; i < roads.size(); i+=1) {
      lines.updateColor(counter, 255, 255, 255, 255);
      lines.updateColor(counter+1, 255, 255, 255, 255);
      counter+=2;
    }
    lines.endUpdateColors();

    p.println("total: " + c);
  }

  public Map<Integer, List<RoadVertex>> getSegments() {
    return segments;
  }


  public void drawGL () {
    GLGraphics renderer = (GLGraphics)p.g;
    GL gl = renderer.gl;
    gl.glEnable(GL.GL_BLEND);
    gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
    renderer.beginGL();
    //lines.setBlendMode(p.ADD);
    renderer.model(lines);
    renderer.endGL();
  }

  public List<RoadEdge> getRoute (String b, String e) {
    List<RoadEdge> segments = DijkstraShortestPath.findPathBetween(net, b, e);

    return segments;
  }

  public List<RoadVertex> getBeginnings() {
    return beginnings;
  }

  public void setBeginnings(List<RoadVertex> beginnings) {
    this.beginnings = beginnings;
  }

  public List<RoadVertex> getEndings() {
    return endings;
  }

  public void setEndings(List<RoadVertex> endings) {
    this.endings = endings;
  }

  public RoadEdge getNewRoad(RoadEdge currentRoad) {
    Random r = new Random();
    RoadEdge result = null;
    Set<RoadEdge> roads = new HashSet<RoadEdge>();
    Set<RoadEdge> out = new HashSet<RoadEdge>();

    roads = net.edgesOf(currentRoad.getTo().getId());

    for (Iterator<RoadEdge> re = roads.iterator(); re.hasNext();) {
      RoadEdge redge = re.next();
      if (redge.getID() != currentRoad.getID()
        && !redge.getHash().equals(currentRoad.getHash())) {
        out.add(redge);
      }
    }

    RoadEdge[] a = out.toArray(new RoadEdge[out.size()]);
    // p.println ("current: " + currentRoad);
    // p.println("all:");
    // p.println(a);
    // p.println ();

    if (a.length > 0) {
      result = a[r.nextInt(a.length)];
      p.println(currentRoad);
      p.println(result + " len: " + a.length);
      p.println(currentRoad.getTo().getX() + " "
        + currentRoad.getTo().getY() + " " + net.edgesOf(currentRoad.getTo().getId()));
      p.println(result.getTo().getX() + " " + result.getTo().getY());
      p.println("of all:");
      p.println(a);
      p.println();
      if (!(currentRoad.getTo().getX() == result.getFrom().getX() && currentRoad
        .getTo().getY() == result.getFrom().getY())) {
        p.println("reversed road!");
        RoadVertex from = result.getFrom();
        RoadVertex to = result.getTo();
        result.setFrom(to);
        result.setTo(from);
      }
    } 
    else
      result = null;

    return result;
  }

  public int getNrRoads(RoadEdge currentRoad) {
    RoadEdge result;
    Set<RoadEdge> outgoingRoads = net.edgesOf(currentRoad.getTo().getId());
    Set<RoadEdge> incomingRoads = net
      .edgesOf(currentRoad.getFrom().getId());
    RoadEdge[] a = outgoingRoads
      .toArray(new RoadEdge[outgoingRoads.size()]);
    RoadEdge[] b = incomingRoads
      .toArray(new RoadEdge[incomingRoads.size()]);
    return a.length + b.length - 2;
  }

  public void draw() {
    //     for (int i = 0; i < crossings.size(); i++) {
    //       crossings.get(i).draw();
    //       p.text(net.edgesOf(crossings.get(i).getId()).size(),crossings.get(i).getX(), crossings.get(i).getY());
    //     }
    for (RoadEdge road : roads) {
      road.draw();
    }

    //      if (this.getNrRoads(road) == 2) {
    //        p.stroke(0, 0, 255);
    //        road.draw();
    //        p.stroke(255, 255, 255);
    //      }
    //      if (this.getNrRoads(road) == 3) {
    //        p.stroke(0, 255, 0);
    //        road.draw();
    //        p.stroke(255, 255, 255);
    //      }
    //      if (this.getNrRoads(road) == 4) {
    //        p.stroke(255, 0, 0);
    //        road.draw();
    //        p.stroke(255, 255, 255);
    //      }
    //      if (this.getNrRoads(road) == 5) {
    //        p.stroke(255, 255, 0);
    //        road.draw();
    //        p.stroke(255, 255, 255);
    //      }
    //      if (this.getNrRoads(road) == 6) {
    //        p.stroke(255, 0, 255);
    //        road.draw();
    //        p.stroke(255, 255, 255);
    //      }
    //      if (this.getNrRoads(road) > 6) {
    //        p.stroke(255, 100, 255);
    //        road.draw();
    //        p.stroke(255, 255, 255);
    //      }
    //    }
  }

  public SimpleGraph<String, RoadEdge> getNet() {
    return net;
  }

  public void setNet(SimpleGraph<String, RoadEdge> net) {
    this.net = net;
  }

  public List<RoadVertex> getCrossings() {
    return crossings;
  }

  public void setCrossings(List<RoadVertex> crossings) {
    this.crossings = crossings;
  }

  public List<RoadEdge> getRoads() {
    return roads;
  }

  public void setRoads(List<RoadEdge> roads) {
    this.roads = roads;
  }
}

