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.ctrl.BeatTimingBehavior;
import net.mkonrad.synes.data.AbstractGraph;
import net.mkonrad.synes.data.Beat;
import net.mkonrad.synes.data.BeatGraph;
import net.mkonrad.synes.data.SynthObj;
import net.mkonrad.synes.gui.SceneCore;
import net.mkonrad.synes.gui.elements.instruments.InstrumentShape;
import net.mkonrad.synes.util.DbgTools;
import net.mkonrad.synes.util.MusicTools;

public class BeatGraphLine extends AbstractGraphLine
{
  private BeatTimingBehavior _beatTiming = null;
  
  private int _currBeatIndex = 0;
  private int _currBeatStep = 1;
  
  public BeatGraphLine(SceneCore sceneCtrl)
  {
    super(sceneCtrl);
  }

  public BeatTimingBehavior getBeatTiming()
  {
    return _beatTiming;
  }

  public void setBeatTiming(BeatTimingBehavior beatTiming)
  {
    _beatTiming = beatTiming;
  }
  
  /**
   * The pointer was clicked. Create a new line segment
   */
  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 pointer
    addLinePoints(synthObj, looseEnd);
    
    //add a new line label
    addLineLabel();
    
    //set the distance between these points to the previous beat
    ((Beat)synthObj).getPrevBeat().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;
    
    SynthObj lastSynth = (SynthObj)_graph.getLast();
    Vector3f pVec = new Vector3f(pointer);
    
    //Calculate the current distance
    _currDistance = pointer.distance(new Point3f(lastSynth.getPos().x, lastSynth.getPos().y, lastSynth.getPos().z));
    
    //update the line segments
    updateLinePoints(lastSynth, pVec);
    
    //update the last line label showing the current beat duration
//    updateLineLabel(0, lastSynth.getPos(), pVec, String.format("%1$.2f", _currDistance));
    updateLineLabel(_lineLabelsTGs.size() - 1, lastSynth.getPos(), pVec, String.format("%1$.2f", ((Beat)lastSynth).getDur()));

    //update the beat's shape to turn with the line
    Vector2f v1 = null, v2 = null, v3 = null;
    Vector3f vTmp = null;
    if (_graph.size() > 1)
    {
      vTmp = ((SynthObj)_graph.get(_graph.size() - 2)).getPos();
      v1 = new Vector2f(vTmp.x, vTmp.y);
    }
    
    vTmp = lastSynth.getPos();
    v2 = new Vector2f(vTmp.x, vTmp.y);
    
    v3 = new Vector2f(pVec.x, pVec.y);
    
    lastSynth.getInstrumentShape().turn(calcAngleBetweenSegments(v1, v2, v3));
    
    //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)
  {
    //get previous and next beat of this SynthObj
    Beat prevBeat = ((Beat)synthObj).getPrevBeat();
    Beat nextBeat = ((Beat)synthObj).getNextBeat();
    
    //if we have a closed graph, then the last and first beat doesn't have the right previous/next beats set
    if (_graph.getState() == BeatGraph.STATE_CLOSED)
    {
      if (_graph.indexOf(synthObj) == 0)
        prevBeat = (Beat)_graph.getLast();
      if (_graph.indexOf(synthObj) == _graph.size() - 1)
        nextBeat = (Beat)_graph.get(0);
    }
    
    //update the segment going to the previous beat
    if (prevBeat != null)
    {
      updateLinePoints(prevBeat, synthObj);
    }
    
    //update the segment going to the next beat
    if (nextBeat != null)
    {
      updateLinePoints(synthObj, nextBeat);
    }
    
    //don't forget to update the geometry data so it becomes visible
    updateGeometry();
  }
  
  /**
   * Make a visual beat
   * This method gets called by BeatTimingBehavior. The current beat flashes
   * on the screen.
   * @return
   */
  public long beat()
  {
    long timeDelay = 0;
    Beat beat = ((BeatGraph)_graph).getBeat(_currBeatIndex);
    
    if (_graph.getState() == BeatGraph.STATE_OPENED && _currBeatStep < 0 && _currBeatIndex > 0) //back way!
    {
      timeDelay = MusicTools.beats2ms(((BeatGraph)_graph).getBeat(_currBeatIndex-1).getDur());
    }
    else
    {
      timeDelay = MusicTools.beats2ms(beat.getDur());
    }
    
    beat.getInstrumentShape().beat();
    
    if (_currBeatIndex >= _graph.size() - 1)  //last beat in line
    {
      if (_graph.getState() == BeatGraph.STATE_OPENED)
      {
        _currBeatStep = -1;
      }
      else
      {
        _currBeatStep = 1;
        _currBeatIndex = -1;
      }
    }

    _currBeatIndex += _currBeatStep;
    
    if (_currBeatIndex < 0 && _graph.getState() == BeatGraph.STATE_OPENED)
    {
      _currBeatIndex = 1;
      _currBeatStep = 1;
    }
   
    return timeDelay;
  }
  
  public void closeBeatGraph()
  {
    _graph.setState(BeatGraph.STATE_CLOSED);
    
    //SynthObj firstNote = (SynthObj)_graph.get(0);
    //SynthObj lastNote = (SynthObj)_graph.get(_graph.size() - 1);
    
    updateLinePoints((SynthObj)_graph.get(_graph.size() - 1), (SynthObj)_graph.get(0));
    
    updateGeometry();
    
    //calcQuadPoints(lastNote.getInstrumentShape().getPos(), lastNote.getAmp() / lineWidthRatio,
    //    firstNote.getInstrumentShape().getPos(), firstNote.getAmp() / lineWidthRatio, _coords.size() - 4); //adjust this quad
    
    //this.setGeometry(_coords);
  }

  protected Color3f calcLineColor(int index)
  {
    int objIndex = Math.round(index * 0.25f);
    
    if (_graph.getState() == BeatGraph.STATE_OPENED)
    {
      if (objIndex < _graph.size())
        return ((SynthObj)_graph.get(objIndex)).getInstrument().getColor();
      else
        return _pointerObj.getInstrument().getColor();
    }
    else
    {
      return ((SynthObj)_graph.get(objIndex % _graph.size())).getInstrument().getColor();
    }
  }
}
