
// Copyright (c) 2001
package uk.co.patrickhaston.mars;

//import java.io.FileReader;
//import java.io.BufferedReader;
//import java.io.FileWriter;
//import java.io.BufferedWriter;
//import java.io.IOException;
import java.io.*;
import java.awt.*;
import java.util.Date;
//import javax.swing.*;
//import java.math.*;
import java.util.Observable;
//import java.util.GregorianCalendar;
import java.text.DateFormat;

/**
 * A Class class.
 * <P>
 * @author Patrick Haston
 */
public class MarsModel extends Observable 
{
  // Parent
  public MarsFrame theFrame = null;
  
  // System stuff
  public String applicationDirectory = "C:\\Mars";
  public String imageDirectory = "images";
  public String dataDirectory = "data";
  public String saveDirectory = "save";
  public String logDirectory = "log";
  public MarsSystem systemResources = null;
  
  // Reference Data
  public MarsResources referenceResources = null;
  public String referenceDataFile;
  public MarsFeatures Features = null;
  public MarsTypes FeatureTypes = null;
  public MarsConstructionTypes ConstructionTypes = null;
  public MarsTechnologies Technologies = null;
  public MarsTypes TechnologyTypes = null;
  public MarsActivityTypes ActivityTypes = null;
  public MarsEnables Enables = null;
  public MarsMessages Messages = null;
  public MarsRoles Roles = null;
  public MarsGoals Goals = null;
  public MarsEquipments Equipment = null;
  public MarsChoices Choices = null;
  public MarsMyStatements MyStatements = null;
  public MarsResponses Responses = null;
  public MarsLinkTypes LinkTypes = null;
  protected MediaTracker mediaTracker = null;
  public MarsPlans Plans = null;
  public MarsTypes PlanTypes = null;
  public MarsTypes Names = null;
  public MarsFaces Faces = null;
  public MarsBiotopes Biotopes = null;
  public MarsPedias marsPedia = null;

  // Game Data
  public MarsConstructions Constructions = null;
  public MarsSettlements Settlements = null;
  public MarsActivities Activities = null;
  public MarsGroups Groups = null;
  public MarsResources gameResources = null;
  public MarsPeople People = null;
  public MarsRegions Regions = null;
  public MarsVariables Variables = null;
  public MarsDiscoveries Discoveries = null;
  public MarsWaypoints Waypoints = null;
  public MarsLinks Links = null;

  // Mars Constants
  public double SolarEnergy = 100;
  public double FinesAbsorbtionFactor = 0.01;
  public double FinesCloudFactor = 0.001;
  public double WaterVapourAbsorbtionFactor = 0.001;
  public double WaterVapourCloudFactor = 0.01;
  public double AirAbsorbtionFactor = 0.001;
  public double AirCloudFactor = 0.0001;

  // Mars Variables
  public double AtmosphereMass;
  public int gameSpeed; // 0 = stopped; 1 = slow, 2 = medium, 3 = fast
  public MarsDate gameDate = null;
  public int workingWeek;
  public int salary;
  public int rent;
  public int AtmosphereResources;
  public double greenhouseEffect = 0.999; // greenhouse effect lets 99.9% of heat escape


  // Flags
  protected int dataType;
  public int gameStatus;
  public static int GAME_NOT_LOADED = 0; 
  public static int GAME_NOT_STARTED = 1; 
  public static int GAME_PLAYING = 2; 
  public static int GAME_PAUSED = 3; 
  public boolean logEnabled = true;

  /**
   * Constructor
   */
  public MarsModel(MarsFrame f)
  {
    theFrame = f;
    init();
//    systemMessage("Application Started");
  }
  
  private void init()
  {
    try
    {
      if( logEnabled )
      {
        this.startLog();
      }
      systemResources = new MarsSystem();
      FeatureTypes = new MarsTypes();
      Features = new MarsFeatures();
      referenceResources = new MarsResources();
      ConstructionTypes = new MarsConstructionTypes(this);
      Technologies = new MarsTechnologies(this);
      TechnologyTypes = new MarsTypes();
      ActivityTypes = new MarsActivityTypes();
      Enables = new MarsEnables(this);
      Messages = new MarsMessages(this);
      Roles = new MarsRoles();
      Goals = new MarsGoals();
      Equipment = new MarsEquipments(this);
      Choices = new MarsChoices();
      MyStatements = new MarsMyStatements();
      Responses = new MarsResponses(this);
      LinkTypes = new MarsLinkTypes(this);
      Plans = new MarsPlans(this);
      PlanTypes = new MarsTypes();
      Names = new MarsTypes();
      Faces = new MarsFaces(this);
      Biotopes = new MarsBiotopes(this);
      marsPedia = new MarsPedias(this);
  
      gameStatus = 0;
      gameSpeed = 0;
      workingWeek = 5;
      gameDate = new MarsDate();
      systemMessage("Mars version 0.905");
      mediaTracker = new MediaTracker(theFrame);
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }
  }

  public boolean LoadReferenceData()
  {
    systemResources.loadResources(this);
    return LoadData(new File(referenceDataFile));
  }

  private void setNewGameVariables()
  {
    Constructions = new MarsConstructions(this);
    Settlements = new MarsSettlements(this);
    Activities = new MarsActivities(this);
    Groups = new MarsGroups();
    gameResources = new MarsResources();
    gameResources.setCollectionName("[Game Resources]");
    People = new MarsPeople(this);
    Regions = new MarsRegions(this);
    Variables = new MarsVariables();
    Discoveries = new MarsDiscoveries(this);
    Waypoints = new MarsWaypoints(this);
    Links = new MarsLinks(this);
    
  }
  
  public void LoadNewEasyGame()
  {
    setNewGameVariables();
    gameStatus = 1;
    systemMessage("Loading " + applicationDirectory + "\\" + dataDirectory +"\\MarsNewEasyGame.txt");
    LoadData(new File(applicationDirectory + "\\" + dataDirectory +"\\MarsNewEasyGame.txt"));

    
    try
    {
      if( logEnabled ) this.logEvent("Loading images...");
      mediaTracker.waitForAll();
      if( logEnabled ) this.logEvent("Images loaded.");
    }
    catch (InterruptedException e)
    {
      e.printStackTrace();
    }
    
    if( logEnabled ) this.logEvent("Creating waypoints...");
    Waypoints.createWaypoints(2);
    if( logEnabled ) this.logEvent("Waypoints created.");
    
    if( logEnabled ) this.logEvent("Creating links...");
    Waypoints.createLinks();
    if( logEnabled ) this.logEvent("Links created.");

    LatLong loc = new LatLong(0,0);
    int wp = Waypoints.nearestWaypoint(loc);
    Settlements.setSettlementLocation(1, wp, (MarsWaypoint) Waypoints.elementAt(wp));
    int erv = Waypoints.nearestWaypoint(Settlements.getLocation(1));
    Settlements.setSettlementLocation(2, erv, (MarsWaypoint) Waypoints.elementAt(erv));
    
    if( logEnabled ) this.logEvent("Resetting waypoints...");
    Constructions.resetWaypoints();

    if( logEnabled ) this.logEvent("Decoding variables...");
    decodeVariables();
    
    if( logEnabled ) this.logEvent("Creating Regions...");
    Regions.create();
    
    if( logEnabled ) this.logEvent("Completed LoadNewEasyGame()");
  }

  public void LoadNewMediumGame()
  {
    setNewGameVariables();

    gameStatus = 1;
    systemMessage("Loading " + applicationDirectory + "\\" + dataDirectory +"\\MarsNewEasyGame.txt");
    LoadData(new File(applicationDirectory + "\\" + dataDirectory +"\\MarsNewEasyGame.txt"));

    try
    {
      mediaTracker.waitForAll();
    }
    catch (InterruptedException e)
    {
      e.printStackTrace();
    }
    
    Waypoints.createWaypoints(3);
    Waypoints.createLinks();

    LatLong loc = new LatLong(0,0);
    int wp = Waypoints.nearestWaypoint(loc);
    Settlements.setSettlementLocation(1, wp, (MarsWaypoint) Waypoints.elementAt(wp));

    decodeVariables();
  }

  public void LoadNewHardGame()
  {
    setNewGameVariables();

    gameStatus = 1;
    systemMessage("Loading " + applicationDirectory + "\\" + dataDirectory +"\\MarsNewEasyGame.txt");
    LoadData(new File(applicationDirectory + "\\" + dataDirectory +"\\MarsNewEasyGame.txt"));
    
    try
    {
      mediaTracker.waitForAll();
    }
    catch (InterruptedException e)
    {
      e.printStackTrace();
    }
    
    Waypoints.createWaypoints(4);
    Waypoints.createLinks();

    LatLong loc = new LatLong(0,0);
    int wp = Waypoints.nearestWaypoint(loc);
    Settlements.setSettlementLocation(1, wp, (MarsWaypoint) Waypoints.elementAt(wp));

    decodeVariables();
  }

  public boolean LoadData(File file)
  {
    String line = null;
    BufferedReader bIn = null;
    boolean outcome = true;
    dataType = 0;

    if( logEnabled ) this.logEvent("Loading " + file);

    try
    {
      bIn = new BufferedReader(new FileReader(file));

      line = bIn.readLine();
      while (line != null)
      {
        if(line.length() > 1 )
        {
         if (line.startsWith("-") )
         {
          // skip this line
         }
         else
         {
          switch (dataType)
          {
            case 0: // find out what section this is
              readSection(line);
              break;
            case 1: // Feature Types
              FeatureTypes.readData(line);
              break;
            case 2: // Features
              Features.readData(line);
              break;
            case 3: // reference Resources
              referenceResources.readData(line);
              break;
            case 4: // Construction Types
              ConstructionTypes.readData(line);
              break;
            case 5: // Technologies
              Technologies.readData(line);
              break;
            case 6: // Technology Types
              TechnologyTypes.readData(line);
              break;
            case 7: // Activity Types
              ActivityTypes.readData(line);
              break;
            case 8: // Constructions
              Constructions.readData(line);
              break;
            case 9: // Settlements
              Settlements.readData(line);
              break;
            case 10: // People
              People.readData(line);
              break;
            case 11: // Activities
              Activities.readData(line);
              break;
            case 12: // Groups
              Groups.readData(line);
              break;
            case 13: // Game Resources
              gameResources.readData(line);
              break;
            case 14: // Regions
              Regions.readData(line);
              break;
            case 15: // Variables
              Variables.readData(line);
              break;
            case 16: // Enables
              Enables.readData(line);
              break;
            case 17: // Discoveries
              Discoveries.readData(line);
              break;
            case 18: // Messages
              Messages.readData(line);
              break;
            case 19: // Roles
              Roles.readData(line);
              break;
            case 20: // Goals
              Goals.readData(line);
              break;
            case 21: // Equipment
              Equipment.readData(line);
              break;
            case 22: // Choices
              Choices.readData(line);
              break;
            case 23: // MyStatements
              MyStatements.readData(line);
              break;
            case 24: // Responses
              Responses.readData(line);
              break;
            case 25: // Waypoints
              Waypoints.readData(line);
              break;
            case 26: // Links
              Links.readData(line);
              break;
            case 27: // Link Types
              LinkTypes.readData(line);
              break;
            case 28: // Plans
              Plans.readData(line);
              break;
            case 29: // Plan Types
              PlanTypes.readData(line);
              break;
            case 30: // Names
              Names.readData(line);
              break;
            case 31: // Faces
              Faces.readData(line);
              break;
            case 32: // Biotopes
              Biotopes.readData(line);
              break;
            case 33: // Biotopes
              marsPedia.readData(line);
              break;
            case -1:
              // do nothing
          }
         }
        }
        else
        {
          // reset data type to 0
          dataType = 0;
        }
        line = bIn.readLine();
      }

      bIn.close();
      systemMessage("Data Loaded");
    }
    catch (FileNotFoundException f)
    {
      outcome = false;
      systemMessage("Game file not found.");
    }
    catch (IOException i)
    {
      outcome = false;
      systemMessage("Error reading game file.");
    }

    return outcome;
  }

  public void LoadGameData()
  {
    setNewGameVariables();

    LoadData(theFrame.gameData);

    decodeVariables();
    
    //Waypoints.createWaypoints(5);
    // Set pointers in the Settlements array
    Settlements.setReferences(this);

    gameStatus = 1;
  }

  public void SaveGameData()
  {
    //String line = null;
    BufferedWriter bOut = null;
    boolean outcome = true;
  
    try
    {
      bOut = new BufferedWriter(new FileWriter(theFrame.gameData));

      Settlements.writeData(bOut);
      Groups.writeData(bOut);
      Activities.writeData(bOut);
      Constructions.writeData(bOut);
      gameResources.writeData(bOut);
      People.writeData(bOut);
      Regions.writeData(bOut);
      encodeVariables();
      Variables.writeData(bOut);
      Messages.writeData(bOut);
      Waypoints.writeData(bOut);
      Links.writeData(bOut);

      bOut.close();
      
    }
    catch (IOException i)
    {
      outcome = false;
    }
  }

  private void readSection(String line)
  {
    dataType = 0;
    if (line.startsWith("[Feature Types]")) dataType = 1;
    if (line.startsWith("[Features]")) dataType = 2;
    if (line.startsWith("[Resources]")) dataType = 3;
    if (line.startsWith("[Construction Types]")) dataType = 4;
    if (line.startsWith("[Technology]")) dataType = 5;
    if (line.startsWith("[Technology Types]")) dataType = 6;
    if (line.startsWith("[Activity Types]")) dataType = 7;
    if (line.startsWith("[Constructions]")) dataType = 8;
    if (line.startsWith("[Settlements]")) dataType = 9;
    if (line.startsWith("[People]")) dataType = 10;
    if (line.startsWith("[Activities]")) dataType = 11;
    if (line.startsWith("[Groups]")) dataType = 12;
    if (line.startsWith("[Game Resources]")) dataType = 13;
    if (line.startsWith("[Regions]")) dataType = 14;
    if (line.startsWith("[Variables]")) dataType = 15;
    if (line.startsWith("[Enables]")) dataType = 16;
    if (line.startsWith("[Discoveries]")) dataType = 17;
    if (line.startsWith("[Messages]")) dataType = 18;
    if (line.startsWith("[Roles]")) dataType = 19;
    if (line.startsWith("[Goals]")) dataType = 20;
    if (line.startsWith("[Equipment]")) dataType = 21;
    if (line.startsWith("[Choices]")) dataType = 22;
    if (line.startsWith("[MyStatements]")) dataType = 23;
    if (line.startsWith("[Responses]")) dataType = 24;
    if (line.startsWith("[Waypoints]")) dataType = 25;
    if (line.startsWith("[Links]")) dataType = 26;
    if (line.startsWith("[Link Types]")) dataType = 27;
    if (line.startsWith("[Plans]")) dataType = 28;
    if (line.startsWith("[Plan Types]")) dataType = 29;
    if (line.startsWith("[Names]")) dataType = 30;
    if (line.startsWith("[Faces]")) dataType = 31;
    if (line.startsWith("[Biotopes]")) dataType = 32;
    if (line.startsWith("[MarsPedia]")) dataType = 33;
    
    if (dataType > 0)
    {
      if( logEnabled ) this.logEvent("Start of reading section type " + dataType);
    }
  }

  private void decodeVariables()
  {
    if (Variables != null)
    {
      if (Variables.size() > 1)
      {
        for (int i = 1; i < Variables.size(); i++)
        {
          MarsVariable v = ((MarsVariable) Variables.elementAt(i));

          if (v.isSameName("AtmosphereMass"))
          {
            AtmosphereMass = v.doubleValue();
          }
  //public int gameSpeed; // 0 = stopped; 1 = slow, 2 = medium, 3 = fast
          if (v.isSameName("gameSpeed"))
          {
            gameSpeed = v.intValue();
          }
  //public MarsDate gameDate = null;
          if (v.isSameName("gameDate"))
          {
            gameDate = v.dateValue();
          }
          
        }
      }
    }
  }

  private void encodeVariables()
  {
    if (Variables != null)
    {
      if (Variables.size() > 1)
      {
        for (int i = 1; i < Variables.size(); i++)
        {
          MarsVariable v = new MarsVariable((MarsVariable) Variables.elementAt(i));

          if (v.isSameName("AtmosphereMass"))
          {
            v.setValue( new String("" + AtmosphereMass));
          }
  //public int gameSpeed; // 0 = stopped; 1 = slow, 2 = medium, 3 = fast
          if (v.isSameName("gameSpeed"))
          {
            v.setValue( new String("" + gameSpeed) );
          }
  //public MarsDate gameDate = null;
          if (v.isSameName("gameDate"))
          {
            DateFormat fmt = DateFormat.getDateInstance(DateFormat.LONG);
            v.setValue( fmt.format( gameDate.getTime() ));
          }

          Variables.setElementAt(v, i);
        }
      }
    }
  }

  public void startGame(int speed)
  {
    if (speed == 1) gameSpeed = 1;
    if (speed == 2) gameSpeed = 2;
    if (speed == 3) gameSpeed = 3;
  }

  // This function moves the world forward one day at a time
  public void nextDay()
  {
    gameDate.roll(gameDate.DAY_OF_YEAR, true);
    if( logEnabled ) logEvent("Starting day " + gameDate.toString());
    Messages.model();

    // Do Climate Modelling
    modelClimate();
    modelBiotopes();

    // Do Activity modelling
    Activities.modelActivities(); // commented out as it is now called from people
    
    // update the existing constructions
    modelConstructions();

    // Model the people
    People.model();
    
  }

  public void run()
  {
  }

  public void modelClimate()
  {
    Regions.update();
  }

  public void modelBiotopes()
  {
  }

  public void modelSettlements()
  {
  }

  public void modelConstructions()
  {
     Constructions.update();
  }

  public void systemMessage(String s)
  {
    Messages.addElement( new MarsMessage (this.gameDate, 0, s) );
    if(theFrame != null)
    {
      if (theFrame.messageList != null)
      {
        theFrame.messageList.setListData(Messages);
      }
    }
    if(logEnabled) logEvent("Message: " + s);
  }

  public void messageFrom(String s, int p)
  {
    MarsMessage m = new MarsMessage (this.gameDate, p, s);
    Messages.addElement( m );
    if(theFrame != null)
    {
      if (theFrame.messageList != null)
      {
        theFrame.messageList.setListData(Messages);
      }
    }
    if(logEnabled) logEvent("Message: " + m.toString());
  }
  
  private void startLog()
  {
    BufferedWriter bOut = null;
    BufferedWriter bErrorOut = null;
    Date d = new Date();
  
    try
    {
      // create a new log file
      bOut = new BufferedWriter(new FileWriter(applicationDirectory + "\\" + logDirectory +"\\EventLog.txt"));
      
      bOut.write(d.toString() + " : Log Started");
      bOut.newLine();

      bOut.close();
      
      // create a new error log file
      bErrorOut = new BufferedWriter(new FileWriter(applicationDirectory + "\\" + logDirectory +"\\ErrorLog.txt"));
      
      bErrorOut.write(d.toString() + " : Log Started");
      bErrorOut.newLine();

      bErrorOut.close();
    }
    catch (IOException i)
    {
      // do something
      //logError("Error encountered in MarsModel.LogError " + i.getMessage());
      i.printStackTrace();
    }
  }

  public void logEvent(String s)
  {
    BufferedWriter bOut = null;
    Date d = new Date();
  
    try
    {
      // Open the log file with append set to true
      bOut = new BufferedWriter(new FileWriter(applicationDirectory + "\\" + logDirectory +"\\EventLog.txt", true));

      // add the message
      bOut.write(d.toString() + " : " + s);
      bOut.newLine();

      bOut.close();
      
    }
    catch (IOException i)
    {
      i.printStackTrace();
    }
  }

  public void logError(String s)
  {
    BufferedWriter bOut = null;
    Date d = new Date();
  
    try
    {
      // Open the log file with append set to true
      bOut = new BufferedWriter(new FileWriter(applicationDirectory + "\\" + logDirectory +"\\ErrorLog.txt", true));

      // add the message
      bOut.write(d.toString() + " : " + s);
      bOut.newLine();

      bOut.close();
      
    }
    catch (IOException i)
    {
      i.printStackTrace();
    }
  }
}


