import java.util.Observable;
import java.util.*;
import java.awt.*;
import javax.swing.*;
import java.io.*;

public class Model extends Observable
{
   /** The observers that are watching this model for changes. */
  private ArrayList<Observer> observers;
  private ArrayList<Thingy> thingies;
  private Thingy curThingy;
  private Color curColour;
  private int curThiccness;
  private int totalLen;
  private int totalVisibleLen;
  public Color sideColour;
  public Color controlColour;

  // Constructor
  public Model()
  {
    this.observers = new ArrayList();
    this.thingies = new ArrayList();
    this.curColour = Color.black;
    this.curThiccness = 5;
    this.totalLen = 0;
    this.totalVisibleLen = 0;
    setChanged();
  }
  
  public void newThingy(int x, int y)
  {
    if (totalLen != totalVisibleLen)
    {
      // resize curthingy to it's visible length
      ArrayList<Point> temp = new ArrayList();
      for (int i = 0; i < curThingy.getVisibleLen(); i++)
      {
        temp.add(curThingy.getPoints().get(i));
      }
      curThingy.setPoints(temp);
      // reset total length to total visible length
      totalLen = totalVisibleLen;
      // delete extra nodes in thingies
      int start = thingies.indexOf(curThingy);
      for (int i = start + 1; i < thingies.size(); i++)
      {
        thingies.remove(i);
      }
    }

    curThingy = new Thingy(curColour, curThiccness);
    thingies.add(curThingy);
    curThingy.addPoint(x, y);
    totalLen++;
    totalVisibleLen++;
    setChanged();
    notifyObservers();
  }
  
  public void addPoint(int x, int y)
  {
    curThingy.addPoint(x, y);
    totalLen++;
    totalVisibleLen++;
    setChanged();
    notifyObservers();
  }
  
  public int getTotalLen()
  {
    return totalLen;
  }
  
  public int getTotalVisibleLen()
  {
    return totalVisibleLen;
  }
  
  public ArrayList<Thingy> getThingies()
  {
    return thingies;
  }
  
  public void changeThingy(int x) // int x is the position in the slider we are at
  {
    /*int tempLen = 0;
    int index = 0;
    
    for (int i = 0; i < thingies.size() && tempLen <= x; i++)
    {
      tempLen += thingies.get(i).getVisibleLen();
      index = i;
    }*/
    
    
    int index = 0;
    int tempLen = 0;
    while (tempLen <= totalVisibleLen && index < thingies.size())
    {
      tempLen += thingies.get(index).getVisibleLen();
      ++index;
    }
    
    //if (Math.abs(index - thingies.indexOf(curThingy)) > 1 && index != (thingies.size() - 1)) index = thingies.indexOf(curThingy);
    curThingy = thingies.get(index);
    // off by one adjustments if we're at the end of a thingy
    if (index > 0 && curThingy.getVisibleLen() == 0 && tempLen > x) curThingy = thingies.get(index - 1);
    if (curThingy.getVisibleLen() == curThingy.getPoints().size() && tempLen < x) curThingy = thingies.get(index + 1);
    
    // now modify tempThingy
    // if we want to go back in time
    if (tempLen > x)
    {
      curThingy.reduceLen();
      totalVisibleLen--;
    }
    else // go forwards
    {
      curThingy.increaseLen();
      totalVisibleLen++;
    }

    setChanged();
    notifyObservers();
  }

  public void ffwd()
  {
    for (Thingy t: thingies)
    {
      t.setVisibleLen(t.getPoints().size());
    }

    if (thingies.size() >= 1)
    {
      curThingy = thingies.get(thingies.size() - 1);
    }

    setChanged();
    notifyObservers();
  }
  
  public void rewind()
  {
    for (Thingy t: thingies)
    {
      t.setVisibleLen(0);
    }

    if (thingies.size() >= 1)
    {
      curThingy = thingies.get(0);
    }

    setChanged();
    notifyObservers();
  }

  public void setSideColour(Color c)
  {
    sideColour = c;
    setChanged();
    notifyObservers();
  }
  
  public void setControlColour(Color c)
  {
    controlColour = c;
    setChanged();
    notifyObservers();
  }
  
  public void changeThiccness(int x)
  {
    curThiccness = x;
    setChanged();
    notifyObservers();
  }
  
  public void changeColour(Color c)
  {
    curColour = c;
    setChanged();
    notifyObservers();
  }
  
  public int getThiccness()
  {
    return curThiccness;
  }
  
  public Color getColour()
  {
    return curColour;
  }

  // Add observer to be notified on change
  public void addObserver(Observer observer)
  {
    this.observers.add(observer);
  }

  // Remove an observer from opdate list
  public void removeObserver(Observer observer)
  {
    this.observers.remove(observer);
  }

   // Notify all observers shit went down
  public void notifyObservers()
  {
    for (Observer observer: this.observers)
    {
      observer.update(this);
    }
  }
}

class Thingy
{
  private ArrayList<Point> points;
  private Color colour;
  private int thiccness;
  private int visibleLen;
  
  public Thingy(Color c, int x)
  {
    points = new ArrayList();
    this.colour = c;
    this.thiccness = x;
    this.visibleLen = 0;
  }
  
  public void addPoint(int x, int y)
  {
    points.add(new Point(x, y));
    visibleLen++;
  }
  
  public ArrayList<Point> getPoints()
  {
    return points;
  }

  public void setPoints(ArrayList a)
  {
    points = a;
  }
  
  public Color getColour()
  {
    return colour;
  }
  
  public int getThiccness()
  {
    return thiccness;
  }
  
  public int getVisibleLen()
  {
    return visibleLen;
  }
  
  public void reduceLen()
  {
    visibleLen--;
  }
  
  public void increaseLen()
  {
    visibleLen++;
  }

  public void setVisibleLen(int x)
  {
    visibleLen = x;
  }
}
