package org.fleen.forsythia.app.grammarEditor.editor_Jig.gridOverlayPainter;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.fleen.forsythia.app.grammarEditor.GE;
import org.fleen.forsythia.app.grammarEditor.editor_Jig.Editor_Jig;
import org.fleen.forsythia.app.grammarEditor.project.jig.ProjectJigSection;
import org.fleen.forsythia.app.grammarEditor.util.UI;
import org.fleen.geom_2D.DPoint;
import org.fleen.geom_2D.DPolygon;
import org.fleen.geom_2D.GD;
import org.fleen.geom_Kisrhombille.KPolygon;
import org.fleen.geom_Kisrhombille.graph.GEdge;
import org.fleen.geom_Kisrhombille.graph.GVertex;

public class GridOverlayPainter implements Serializable{
  
  private static final long serialVersionUID=5607779324837953808L;

  public void paint(Graphics2D graphics,int w,int h,double scale,double centerx,double centery){
    graphics.setRenderingHints(UI.RENDERING_HINTS);
    GE.ge.editor_jig.editedjig.getJigEditorGeometryCache().update(w,h,scale,centerx,centery);
    if(GE.ge.editor_jig.mode==Editor_Jig.MODE_CREATE_B||GE.ge.editor_jig.mode==Editor_Jig.MODE_RETOUCH){
      renderJigModel_EditSections(graphics);
    }else{//GE.editor_jig.mode==Editor_Jig.MODE_EDITGEOMETRY
      renderJigModel_EditGeometry(graphics);}}
  
  /*
   * ################################
   * RENDER JIG MODEL FOR EDIT GEOMETRY MODE
   * ################################
   */
  
  private void renderJigModel_EditGeometry(Graphics2D graphics){
    try{
      fillSections_EditGeometry(graphics);
      strokeGraphEdges_EditGeometry(graphics);
      renderVertices_EditGeometry(graphics);
    }catch(Exception x){
      x.printStackTrace();}}
  
  private void fillSections_EditGeometry(Graphics2D graphics){
    Color color;
    Path2D path;
    for(KPolygon m:GE.ge.editor_jig.editedjig.getGraph().getDisconnectedGraph().getUndividedPolygons()){
      color=UI.EDITJIG_EDITGEOMETRY_HOSTMETAGONFILLCOLOR;
      path=GE.ge.editor_jig.editedjig.getJigEditorGeometryCache().getPath(m);
      graphics.setPaint(color);
      graphics.fill(path);}}
  
  private void strokeGraphEdges_EditGeometry(Graphics2D graphics){
    graphics.setStroke(UI.GRID_DRAWINGSTROKE);
    graphics.setPaint(UI.EDITJIG_EDITGEOMETRY_STROKECOLOR);
    Iterator<GEdge> i=GE.ge.editor_jig.editedjig.getGraph().edges.iterator();
    GEdge e;
    double[] p0,p1;
    Path2D path=new Path2D.Double();
    while(i.hasNext()){
      e=i.next();
      p0=GE.ge.editor_jig.editedjig.getJigEditorGeometryCache().getPoint(e.v0.kvertex);
      p1=GE.ge.editor_jig.editedjig.getJigEditorGeometryCache().getPoint(e.v1.kvertex);
      path.reset();
      path.moveTo(p0[0],p0[1]);
      path.lineTo(p1[0],p1[1]);
      graphics.draw(path);}}
  
  /*
   * ++++++++++++++++++++++++++++++++
   * RENDER VERTICES
   * ++++++++++++++++++++++++++++++++
   */
  
  private void renderVertices_EditGeometry(Graphics2D graphics){
    renderDefaultVertices(graphics,UI.EDITJIG_EDITGEOMETRY_STROKECOLOR);
    renderHeadDecorations(graphics);}
  
  private void renderDefaultVertices(Graphics2D graphics,Color color){
    graphics.setPaint(color);
    float span=UI.GRID_DEFAULTVERTEXSPAN;
    double[] p;
    Ellipse2D dot=new Ellipse2D.Double();
    for(GVertex v:GE.ge.editor_jig.editedjig.getGraph().vertices){
      p=GE.ge.editor_jig.editedjig.getJigEditorGeometryCache().getPoint(v.kvertex);
      dot.setFrame(p[0]-span/2,p[1]-span/2,span,span);
      graphics.fill(dot);}}
  
  private void renderHeadDecorations(Graphics2D graphics){
    graphics.setStroke(UI.EDITJIG_EDITGEOMETRY_HEADVERTEXDECORATIONSTROKE);
    int span=UI.EDITJIG_EDITGEOMETRY_HEADVERTEXDECORATIONSPAN;
    double[] p;
    if(GE.ge.editor_jig.connectedhead!=null){
      p=GE.ge.editor_jig.editedjig.getJigEditorGeometryCache().getPoint(GE.ge.editor_jig.connectedhead);
      graphics.setPaint(UI.EDITJIG_EDITGEOMETRY_CONNECTEDHEADVERTEXDECORATIONCOLOR);
      graphics.drawOval(((int)p[0])-span/2,((int)p[1])-span/2,span,span);
    }else if(GE.ge.editor_jig.unconnectedhead!=null){
      p=GE.ge.editor_jig.editedjig.getJigEditorGeometryCache().getPoint(GE.ge.editor_jig.unconnectedhead);
      graphics.setPaint(UI.EDITJIG_EDITGEOMETRY_UNCONNECTEDHEADVERTEXDECORATIONCOLOR);
      graphics.drawOval(((int)p[0])-span/2,((int)p[1])-span/2,span,span);}}
  
  /*
   * ################################
   * RENDER JIG MODEL FOR EDIT SECTIONS MODE
   * ################################
   */
  
  private void renderJigModel_EditSections(Graphics2D graphics){
    fillSections_EditSections(graphics);
    strokePolygonEdges_EditSections(graphics);
    renderGlyphs_EditSections(graphics);}
  
  /*
   * Fill color reflects chorus index. A rainbow.
   * 0 is red, 1 is orangyred and so on 
   */
  private void fillSections_EditSections(Graphics2D graphics){
    int colorindex;
    Color color;
    Path2D path;
    for(ProjectJigSection m:GE.ge.editor_jig.editedjig.sections){
      colorindex=m.chorusindex;
      color=UI.EDITJIG_EDITSECTIONS_SECTIONFILL[colorindex%UI.EDITJIG_EDITSECTIONS_SECTIONFILL.length];
      path=GE.ge.editor_jig.editedjig.getJigEditorGeometryCache().getPath(m.getPolygon());
      graphics.setPaint(color);
      graphics.fill(path);}}
  
  /*
   * focus section gets stroked in one color, all of the unfocus sections get stroked in another
   * colors probably match for glyph
   */
  private void strokePolygonEdges_EditSections(Graphics2D graphics){
    graphics.setStroke(UI.GRID_DRAWINGSTROKE);
    graphics.setPaint(UI.EDITJIG_EDITSECTIONS_UNFOCUSSTROKECOLOR);
    Path2D path;
    for(ProjectJigSection m:GE.ge.editor_jig.editedjig.sections){
      if(m==GE.ge.editor_jig.focussection)continue;
      path=GE.ge.editor_jig.editedjig.getJigEditorGeometryCache().getPath(m.getPolygon());
      graphics.draw(path);}
    //focus
    graphics.setPaint(UI.EDITJIG_EDITSECTIONS_FOCUSSTROKECOLOR);
    path=GE.ge.editor_jig.editedjig.getJigEditorGeometryCache().getPath(GE.ge.editor_jig.focussection.getPolygon());
    graphics.draw(path);}
  
  /*
   * ################################
   * RENDER GLYPHS FOR EDIT SECTIONS
   * The focus polygon gets a system of glyphs within its edge that indicate
   * the form of the anchor. That is, v0 and twist.
   * we indicate vertex0 with a dot
   * we indicate twist with a long bendy arrow 
   * ################################
   */
  
  private void renderGlyphs_EditSections(Graphics2D graphics){
    //get non-focus section polygons
    List<DPolygon> nonfocussections=new ArrayList<DPolygon>();
    for(ProjectJigSection section:GE.ge.editor_jig.editedjig.sections)
      if(section!=GE.ge.editor_jig.focussection)
        nonfocussections.add(GE.ge.editor_jig.editedjig.getJigEditorGeometryCache().getDPolygon(section.getPolygon()));
    //get focus section polygon
    DPolygon focussection=GE.ge.editor_jig.editedjig.getJigEditorGeometryCache().getDPolygon(GE.ge.editor_jig.focussection.getPolygon());
    //render non-focus section polygons
    for(DPolygon nonfocussection:nonfocussections)
      renderGlyphs(graphics,nonfocussection,UI.EDITJIG_EDITSECTIONS_UNFOCUSSTROKECOLOR);
    //render focus section polygon
    renderGlyphs(graphics,focussection,UI.EDITJIG_EDITSECTIONS_FOCUSSTROKECOLOR);}
  
  private void renderGlyphs(Graphics2D graphics,DPolygon polygon,Color color){
    GlyphSystemModel glyphsystemmodel=new GlyphSystemModel(
      polygon,
      UI.EDITJIG_EDITSECTIONS_GLYPHINSET);
    if(glyphsystemmodel.isValid()){
      //render v0 dot
      renderV0Dot(graphics,glyphsystemmodel,color);
      //render arrow shaft
      graphics.setStroke(UI.GRID_DRAWINGSTROKE);
      graphics.setPaint(color);
      Path2D path =getPath2D(glyphsystemmodel.glyphpath);
      graphics.draw(path);
      //render arrow head
      renderArrowHead(graphics,glyphsystemmodel,color);}}
    
  /*
   * ++++++++++++++++++++++++++++++++
   * RENDER V0 DOT
   * ++++++++++++++++++++++++++++++++
   */
  
  private void renderV0Dot(Graphics2D graphics,GlyphSystemModel glyphsystemmodel,Color color){
    double dotradius=UI.EDITJIG_EDITSECTIONS_GLYPHV0DOTRADIUS*UI.EDITJIG_EDITSECTIONS_GLYPHINSET;
    DPoint pv0=glyphsystemmodel.getV0DotPoint();
    graphics.setPaint(color);
    Ellipse2D dot=new Ellipse2D.Double(pv0.x-dotradius,pv0.y-dotradius,dotradius*2,dotradius*2);
    graphics.fill(dot);}
  
  /*
   * ++++++++++++++++++++++++++++++++
   * RENDER ARROW HEAD
   * ++++++++++++++++++++++++++++++++
   */
  
  private void renderArrowHead(Graphics2D graphics,GlyphSystemModel glyphsystemmodel,Color color){
    graphics.setPaint(color);
    DPoint 
      p0=glyphsystemmodel.glyphpath.get(glyphsystemmodel.glyphpath.size()-2),
      p1=glyphsystemmodel.glyphpath.get(glyphsystemmodel.glyphpath.size()-1);
    double forward=p0.getDirection(p1);
    DPoint 
      forewardpoint=p1.getPoint(
        forward,
        UI.EDITJIG_EDITSECTIONS_GLYPHARROWLENGTH*UI.EDITJIG_EDITSECTIONS_GLYPHINSET),
      leftpoint=p1.getPoint(
        GD.normalizeDirection(forward-GD.HALFPI),
        UI.EDITJIG_EDITSECTIONS_GLYPHARROWWIDTH*UI.EDITJIG_EDITSECTIONS_GLYPHINSET/2),
      rightpoint=p1.getPoint(
        GD.normalizeDirection(forward+GD.HALFPI),
        UI.EDITJIG_EDITSECTIONS_GLYPHARROWWIDTH*UI.EDITJIG_EDITSECTIONS_GLYPHINSET/2);
    Path2D triangle=new Path2D.Double();
    triangle.moveTo(leftpoint.x,leftpoint.y);
    triangle.lineTo(forewardpoint.x,forewardpoint.y);
    triangle.lineTo(rightpoint.x,rightpoint.y);
    triangle.closePath();
    graphics.fill(triangle);}
  
  //an open path
  private Path2D getPath2D(List<DPoint> points){
    Path2D path2d=new Path2D.Double();
    DPoint p=points.get(0);
    path2d.moveTo(p.x,p.y);
    int s=points.size();
    for(int i=1;i<s;i++){
      p=points.get(i);
      path2d.lineTo(p.x,p.y);}
    return path2d;}
  
}