//////////////////////////////////////////////////////////////////////////
//  DCC++ CONTROLLER
//  COPYRIGHT (c) 2013-2015 Gregg E. Berman
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see http://www.gnu.org/licenses
//
//////////////////////////////////////////////////////////////////////////
//      
//  DCC++ CONTROLLER is a Java program written using the 64-bit Processing Library
//  and Processing IDE (version 3.01).
//
//  DCC++ CONTROLLER provides users with a fully customizeable graphical
//  front end for the total control of model trains and model train layouts
//  via its companion program, DCC++ BASE STATION.
//
//  DCC++ BASE STATION allows a standard Arduino Uno with an Arduino Motor Shield
//  to be used as a fully-functioning digital command and control (DCC) base station
//  for controlling model train layouts that conform to current National Model
//  Railroad Association (NMRA) DCC standards.
//
//  DCC++ CONTROLLER communicates with DCC++ BASE STATION using simple text commands sent
//  via a standard USB Serial Cord at speeds of up to 115200 Baud.  A Bluetooth Wireless
//  Connection may be used in place of a USB Serial Cord without any software modification.
//
//  This version of DCC++ CONTROLLER supports:
//
//  * Multi-Cab / Multi-Throttle configurations using 128-step speed control
//  * 2-byte and 4-byte cab addresses
//  * Customizable cab function buttons F0-F12
//  * User-created multi-layout track plan
//  * Customizeable turnouts and crossovers with controls integrated into track plan
//  * Customizeable routes with configurable buttons
//  * Customizeable routes with route buttons integrated into track plan
//  * Master Power Button
//  * Customizable key-controls
//  * Real-time current monitor
//  * Optional track-integrated sensors
//  * Optional user-created Auto Pilot routines (when used with track-integrated sensors)
//  * Manual activation/de-activation of accessory functions using 512 addresses, each with 4 sub-addresses
//  * Programming on the Main Operations Track
//      - write configuration variable bytes
//      - set/clear specific configuration variable bits
//  * Programming on the Programming Track
//      - write configuration variable bytes
//      - read configuration variable bytes
//
//  With the exception of a standard 15V power supply for the Arduino Uno that can
//  be purchased in any electronics store, no additional hardware is required.
//  
//  Neither DCC++ BASE STATION nor DCC++ CONTROLLER use any known proprietary or
//  commercial hardware, software, interfaces, specifications, or methods related
//  to the control of model trains using NMRA DCC standards.  Both programs are wholly
//  original, developed by the author, and are not derived from any known commercial,
//  free, or open-source model railroad control packages by any other parties.
//  
//  However, DCC++ BASE STATION and DCC++ CONTROLLER do heavily rely on the IDEs and
//  embedded libraries associated with Arduino and Processing.  Tremendous thanks to those
//  responsible for these terrific open-source initiatives that enable programs like
//  DCC++ to be developed and distributed in the same fashion.
//  
//  REFERENCES:
//
//    NMRA DCC Standards:          http://www.nmra.org/standards/DCC/standards_rps/DCCStds.html
//    Arduino:                     http://www.arduino.cc/
//    Processing:                  http://processing.org/
//    GNU General Public License:  http://opensource.org/licenses/GPL-3.0
//
//////////////////////////////////////////////////////////////////////////

import processing.serial.*;
import processing.net.*; 
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.*;

final String   CONTROLLER_VERSION  = "3.0";
final int      BASE_BAUD       =    115200;
final int      SCREEN_WIDTH    =    1366;
final int      SCREEN_HEIGHT   =    768;
final String   STATUS_FILE     =    "dccStatus.xml";

//////////////////////////////////////////////////////////////////////////

void settings(){
  size(SCREEN_WIDTH,SCREEN_HEIGHT);
}

//////////////////////////////////////////////////////////////////////////

void setup(){
  Initialize();
}

//////////////////////////////////////////////////////////////////////////

void draw(){
  
  background(backgroundColor);
        
  for(DccComponent dcc : dccComponents)
    dcc.display();
  
  if(frameCount==1)    // if this is the first frame, just display components and return (otherwise user stare at a blank screen while serial is opening
    return;
    
  if(frameCount==2)    // is this is the second frame, open the serial port --- screen will have already been displayed in prior frame
    aPort.open(arduinoPortXML.getContent());
          
  for(int i=buttonQueue2.size()-1;i>=0;i--){
    buttonQueue2.get(i).init();
    buttonQueue2.remove(i);
  }

  for(int i=buttonQueue.size()-1;i>=0;i--){
    buttonQueue2.add(buttonQueue.get(i));;
    buttonQueue.remove(i);
  }

  if(!mousePressed){
    cursorType=ARROW;
    previousComponent=selectedComponent;
    selectedComponent=null;

    int nComponents = dccComponents.size();
    
    for(int i=nComponents-1;i>=0;i--)
      dccComponents.get(i).check();
            
    cursor(cursorType);
  }
    
  int m=millis();
  if(m-lastTime>250 && aPort!=null && currentMeter.isOn){
    lastTime=m;
    aPort.write("<c>");
  }
  
  msgBoxClock.setMessage(nf(hour(),2)+":"+nf(minute(),2)+":"+nf(second(),2));
  
  if(saveXMLFlag){
    try{
      saveXML(dccStatusXML,STATUS_FILE);
      saveXMLFlag=false;
    } catch(Exception e){
      println("Couldn't save. Will retry");
    }
  }
  
  autoPilot.safetyCheck();
    
} // draw

//////////////////////////////////////////////////////////////////////////

abstract class DccComponent{
  Window window=null;
  int xPos, yPos;
  String componentName="NAME NOT DEFINED";
  abstract void display();
  void check(){};
  void pressed(){};
  void rightClick(){};
  void shiftPressed(){};
  void released(){};
  void drag(){};
  void init(){};
  
  protected int xWindow(){
    if(window==null)
      return 0;
    return window.xPos;
  }
  
  protected int yWindow(){
    if(window==null)
      return 0;
    return window.yPos;
  }
}

//////////////////////////////////////////////////////////////////////////

interface CallBack{
  void execute(int n, String c);
}

//////////////////////////////////////////////////////////////////////////