package net.mkonrad.synes.gui.elements;

import javax.vecmath.Color3f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector2f;
import javax.vecmath.Vector3f;

import net.mkonrad.synes.data.AbstractGraph;
import net.mkonrad.synes.data.Beat;
import net.mkonrad.synes.data.Modulator;
import net.mkonrad.synes.data.SynthGraph;
import net.mkonrad.synes.data.SynthObj;
import net.mkonrad.synes.gui.SceneCore;
import net.mkonrad.synes.util.DbgTools;

public class SynthGraphLine extends AbstractGraphLine
{  
  public SynthGraphLine(SceneCore sceneCtrl)
  {
    super(sceneCtrl);
  }

  /**
   * The pointer was clicked. Create a new line segment from the Modulator to
   * the new pointer
   */
  public void clicked(SynthObj synthObj)
  {
    //create a new loose end
    Vector3f looseEnd = new Vector3f(_lastMovePoint);
    looseEnd.add(new Point3f(0.01f, 0f, 0f)); //shift it a bit
    
    //add a new line segment between the added beat and the modulator
    Modulator addedMod = null, oldMod = null;
    try
    {
      addedMod = (Modulator) synthObj;  //we just added a new modulator
    }
    catch (ClassCastException e)
    {
      oldMod = ((SynthGraph)_graph).getModulator(); //the old one MUST be set then!
    }
    
    if (addedMod == null)
      addLinePoints(oldMod, looseEnd);
    else
      addLinePoints(addedMod, looseEnd);
    
    //add a new line label
    addLineLabel();
    
    //set the distance between this point and the modulator to the SynthObj
    if (addedMod != null)
      ((SynthObj)_graph.get(0)).setDistance(_currDistance);
    else
      synthObj.setDistance(_currDistance);
    
    //show the changes
    updateGeometry();
  }

  /**
   * The pointer has been moved. Update the last graph segment and the pointer
   * shape
   */
  public void movedPointer(Point3f pointer)
  {
    if (_graph.size() < 1)
      return;
    
    if (pointer == null && _lastMovePoint != null)
      pointer = _lastMovePoint;
    
    //get the synth that is connected with the pointer
    SynthObj connectionSynth = ((SynthGraph)_graph).getModulator();
    boolean modInHand = false;
    if (connectionSynth == null)  //if there is no modulator, we have it in the hand!
    {
      connectionSynth = (SynthObj)_graph.get(0);
      modInHand = true;
    }
    
    Vector3f pVec = new Vector3f(pointer);
    
    //Calculate the current distance
    _currDistance = calcDistance(pointer, connectionSynth.getPos());
    
    if (modInHand)
      connectionSynth.setDistance(_currDistance);
    else
      _pointerObj.setDistance(_currDistance);
    
    //update the line segments
    updateLinePoints(((_graph.size() < 2) ? connectionSynth : null), _pointerObj);
    
    //update the last line label showing the current distance to the modulator
    updateLineLabel(_lineLabelsTGs.size() - 1, connectionSynth.getPos(), pVec, String.format("%1$.2f", _currDistance));

    //update the synth's shape to turn with the line
//    connectionSynth.getInstrumentShape().turn(calcAngleBetweenSegments(
//        null,
//        new Vector2f(connectionSynth.getPos().x, connectionSynth.getPos().y),
//        new Vector2f(pVec.x, pVec.y)
//    ));
    
    //remember the last movement
    _lastMovePoint = pointer;
    
    //show the changes
    updateGeometry();
    
    //update the pointer shape position
    if (_pointerObj.getInstrumentShape().getBG().getParent() == null)
      _bg.addChild(_pointerObj.getInstrumentShape().getBG());
    _pointerObj.getInstrumentShape().setPos(pointer);
  }

  /**
   * An object in the graph has been moved. Update the line segments that are
   * connected with this object
   */
  public void movedObj(SynthObj synthObj)
  {
    //check if the modulator has been moved
    Modulator movedMod = null, fixedMod = null;
    try
    {
      movedMod = (Modulator) synthObj;  //we just added a new modulator
    }
    catch (ClassCastException e)
    {
      fixedMod = ((SynthGraph)_graph).getModulator();
    }
    
    float distance = 0.0f;
    
    //update the line segments...
    if (movedMod == null)  //... if a normal SynthObj has been moved
    {
      updateLinePoints(null, synthObj);
      synthObj.setDistance(updateDistance(0, fixedMod.getPos(), synthObj.getPos()));
    }
    else //... or if a Modulator has been moved...
    {
      //... then change all the lines connected to it!
      int lblIndex = 0;
      for (int i = 0; i < _graph.size(); i++)
      {
        SynthObj lineObj = ((SynthGraph)_graph).getSynthObj(i);
        if (lineObj != movedMod)
        {
          lineObj.setDistance(updateDistance(lblIndex, movedMod.getPos(), lineObj.getPos()));
          updateLinePoints(null, lineObj);
          lblIndex++;
        }
      }
    }
    
    if (_lineLabelsBG.getParent() == null)
      _bg.addChild(_lineLabelsBG);
    
    //don't forget to update the geometry data so it becomes visible
    updateGeometry();
  }

  private float updateDistance(int i, Vector3f pos1, Vector3f pos2)
  {
    float distance = calcDistance(pos1, pos2);

    if (_lineLabelsTGs.size() - 1 < i)
      addLineLabel();
    
    updateLineLabel(i, pos1, pos2, String.format("%1$.2f", distance));
    
    return distance;
  }

  protected Color3f calcLineColor(int index)
  {
    if (index > 3 && (index % 4 == 0 || index % 4 == 1))
    {
      return ((SynthGraph)_graph).getModulator().getInstrument().getColor();
    }
    else
    {
      int objIndex = Math.round(index * 0.25f);
      
      if (objIndex < _graph.size())
        return ((SynthObj)_graph.get(objIndex)).getInstrument().getColor();
      else
        return _pointerObj.getInstrument().getColor();
    }
  }
}
