import processing.core.*; 
import processing.data.*; 
import processing.event.*; 
import processing.opengl.*; 

import java.util.HashMap; 
import java.util.ArrayList; 
import java.io.File; 
import java.io.BufferedReader; 
import java.io.PrintWriter; 
import java.io.InputStream; 
import java.io.OutputStream; 
import java.io.IOException; 

public class Molberegner extends PApplet {

/*
 * Dette program er i al sin enkelthed en molberegner, beregnet til at udregne den
 * samlede molv\u00e6gt af en bestemt sammens\u00e6tning af elementer.
 * Programmet er skrevet i forbindelse med et eksamensprojekt i programmering C
 * Koden og programmet m\u00e5 frit benyttes ved angivelse af kilde.
 * Skrevet af Malte Ravn Spence (malteravn95@gmail.com)
 */

// Deklarer de globale variabler.
XML xml;
Element[] e;
int listlength;
int[] count;
float[] sw;
float lw;
float weight;
float space;
PFont font;
boolean start = true;
boolean lang = false;

public void setup()
{
  // Definer sk\u00e6rmst\u00f8rrelse, stroke og font.
  size(1191, 637);
  noStroke();
  font = loadFont("HelveticaNeueLTCom-LtEx-30.vlw");
  textFont(font);

  // Importer data fra ptable.xml.
  xml = loadXML("ptable.xml");
  XML[] children = xml.getChildren("element");
  listlength = children.length;

  // deklarer nye arrays.
  e = new Element[listlength];
  count = new int[listlength];
  sw = new float[listlength];

  for (int i = 0; i < listlength; i++)
  {
    // Hent de forskellige attributter fra hvert 
    //element og assign dem til de midlertidige variabler.
    int id = children[i].getInt("id");
    int gr = children[i].getInt("group");
    int pe = children[i].getInt("period");
    String sh = children[i].getString("short");
    String ty = children[i].getString("type");
    String en = children[i].getString("en");
    String fu = children[i].getContent();
    float mw = children[i].getFloat("molarweight");

    // Lav derefter et nyt objekt med de ovenst\u00e5ende variabler.
    e[i] = new Element(id, gr, pe, sh, ty, en, fu, mw);
    textSize(30);
    sw[i] = textWidth(sh);
  }
}

public void draw()
{
  //Hvis start == true, tegnes hele interfacet, og start 
  //s\u00e6ttes til false, s\u00e5ledes at det ikke tegnes igen.
  if (start == true)
  {
    PtableObject();   
    show();
    start = false;
  }
}

public void PtableObject() // Tegn hele interfacet.
{
  background(20, 20, 20);
  for (int i = 0; i < listlength; i++)
  {
    e[i].draw(); // Tegn alle element-boksene
  }

  // Tegn de to gr\u00e5 bokse for hhv. molekylestruktur og molv\u00e6gt.
  fill(107, 107, 107);
  rect(186, 149, 408, 40);
  rect(597, 149, 153, 40);

  // Skriv "molekyle", "reset" og "g/mol" over de to gr\u00e5 bokse.
  // hvis lang == true skrives molecule & mole i stedet for molekyle og mol.
  fill(107, 107, 107);
  textSize(20);
  if(lang == true)
  {
    textAlign(LEFT, BOTTOM);
    text("molecule", 186, 146);
    textAlign(RIGHT, BOTTOM);
    text("reset", 594, 146);
    text("g/mole", 747, 146);
  }
  else
  {
    textAlign(LEFT, BOTTOM);
    text("molekyle", 186, 146);
    textAlign(RIGHT, BOTTOM);
    text("reset", 594, 146);
    text("g/mol", 747, 146);
  }

  // Tegn de to bokse med hhv. "*" og "**" der hvor 
  // "La" og "Ac" burde v\u00e6re i det periodiske system.
  fill(201, 73, 90);
  rect(156, 345, 60, 60);
  fill(201, 32, 55);
  rect(156, 408, 60, 60);
  fill(20, 20, 20);
  textAlign(CENTER, CENTER);
  textSize(30);
  text("*", 186, 380);
  text("**", 186, 443);
  
  // Skriv credits
  fill(107, 107, 107);
  textSize(15);
  textAlign(LEFT, BOTTOM);
  text("2013", 10, 617);
  textAlign(LEFT, TOP);
  text("malteravn95@gmail.com", 10, 617);
  
  // Engelsk eller Dansk skrives i nederste h\u00f8jre hj\u00f8rne
  // afh\u00e6ngig af lang-variablen.
  if(lang == true)
  {
    lw = textWidth("English");
    textAlign(RIGHT, TOP);
    text("English", 1181, 617); 
  }
  else
  {
    lw = textWidth("Dansk");
    textAlign(RIGHT, TOP);
    text("Dansk", 1181, 617);
  }
}

public void mouseClicked()
{
  // Genoptegn de to lysgr\u00e5 bokse og "slet" evt. tidligere tekst.
  fill(107, 107, 107);
  rect(186, 149, 408, 40);
  rect(597, 149, 153, 40);

  for (int i = 0; i < listlength; i++)
  {
    e[i].clicked();
  }
  
  if (mouseX >= 1181-lw && mouseY >= 612 && mouseX <= 1181 && mouseY <= 627)
  {
    if(lang == true)
    {
      lang = false;
      start = true;
    }
    else if(lang == false)
    {
      lang = true;
      start = true;
    }
  }
  show();
}

public void show()
{
  for (int i = 0; i < listlength; i++)
  {
    e[i].display();
  }
  // Skriv molv\u00e6gten indenfor den h\u00f8jre lysegr\u00e5 boks.
  fill(20, 20, 20);
  textAlign(RIGHT, BOTTOM);
  textSize(30);
  text(weight, 747, 190);
  
  /*
   * Overflow control - hvis afstanden space bliver st\u00f8rre end boksens 
   * l\u00e6ngde, eller v\u00e6gten overstiger 9700, stoppes loopen.
   * Ved tryk p\u00e5 reset nulstilles programmet.
   */
  if (space >= 350 || weight > 9700)
  {
    noLoop();
    start = true;

    // Overskriv reset i r\u00f8d skrift.
    fill(252, 3, 3);
    textSize(20);
    textAlign(RIGHT, BOTTOM);
    text("reset", 594, 146);
  }
  
  // Nulstil weight og space.
  weight = 0;
  space = 0;
}
class Element
{
  //Deklarer de lokale variabler.
  int x;
  int y;
  int id;
  int group;
  int period;
  String shortvers;
  String type;
  String english;
  String fullname;
  float molarweight;
  float spcSpace;
  int col;

  Element(int tid, int tgr, int tpe, String tsh, String tty, String ten, String tfu, float tmw)
  {
    //Assign variablerne hentet fra ptable.xml til de lokale variabler defineret ovenfor.
    id = tid;
    group = tgr;
    period = tpe;
    shortvers = tsh;
    type = tty;
    english = ten;
    fullname = tfu;
    molarweight = tmw;

    x = (group - 1) * 63 + 30; //Lokal x-koordinat baseret p\u00e5 gruppenummer.
    y = (period - 1) * 63 + 30; //Lokal y-koordinat baseret p\u00e5 periodenummer.

    // Hvis typen er enten Lanthanoid eller Actinoid, skal de tegnes 
    // 136px under de andre elementer med samme periodenummer.
    if (type.equals("Lanthanoid") || type.equals("Actinoid"))
    {
      y = y + 136;
    }
    
    // Unders\u00f8g hvilken type elementet har, s\u00e6t derefter col-variablen til en bestemt farve.
    if (type.equals("Alkali-metal"))
    {
      col = color(178, 100, 4);
    }
    else if (type.equals("Alkaline-metal"))
    {
      col = color(178, 125, 20);
    }
    else if (type.equals("Transition-metal"))
    {
      col = color(203, 115, 125);
    }
    else if (type.equals("Lanthanoid"))
    {
      col = color(201, 73, 90);
    }
    else if (type.equals("Actinoid"))
    {
      col = color(201, 32, 55);
    }
    else if (type.equals("Post-transition"))
    {
      col = color(85, 154, 167);
    }
    else if (type.equals("Metalloids"))
    {
      col = color(137, 184, 193);
    }
    else if (type.equals("Non-metal"))
    {
      col = color(128, 167, 0);
    }
    else if (type.equals("Halogen"))
    {
      col = color(156, 196, 40);
    }
    else if (type.equals("Noble-gass"))
    {
      col = color(153, 51, 205);
    }
    else // Hvis elementtypen enten er ukendt, eller ikke h\u00f8rer under de andre typer.
    {
      col = color(107, 107, 107);
    }
  }

  public void draw() // Tegn den farvede boks med id og navn.
  {
    // Tegn en boks med farve-variablen col.
    fill(col);
    rect(x, y, 60, 60);
    fill(20, 20, 20);

    // Skriv Id i \u00f8verste venstre hj\u00f8rne af boksen.    
    textAlign(TOP);
    textSize(10);
    text(id + 1, x + 2, y + 10); // Der l\u00e6gges 1 til id, da id i ptable.xml starter ved 0

    // Skriv det kemiske symbol i midten af boksen.
    textAlign(CENTER, CENTER);
    textSize(30);
    text(shortvers, x + 30, y + 30);
    if(english != "" && lang == true)
    {
      // Skriv det fulde navn i nederste h\u00f8jre hj\u00f8rne af boksen.
      textAlign(RIGHT, BOTTOM);
      textSize(8);
      text(english, (x + 60) - 2, (y + 60) - 2);
    }
    else
    {
      // Skriv det fulde navn i nederste h\u00f8jre hj\u00f8rne af boksen.
      textAlign(RIGHT, BOTTOM);
      textSize(8);
      text(fullname, (x + 60) - 2, (y + 60) - 2); 
    }
  }

  public void clicked() //N\u00e5r musen trykkes ned er der tre mulige events, count++, count-- og RESET.
  {
    // Hvis musen er over den farvede boks n\u00e5r der klikkes, l\u00e6gges 1 til count[i].
    if (mouseX >= x && mouseY >= y && mouseX <= x + 60 && mouseY <= y + 60)
    {
      count[id]++;
    }

    //Hvis musen er over dette element i det gr\u00e5 "molekyle" felt og count[i] 
    //er st\u00f8rre end 0 n\u00e5r der klikkes, tr\u00e6kkes 1 fra count[i].
    if (mouseX >= 189 + spcSpace && mouseY >= 146 && mouseX <= 189 + spcSpace + sw[id] && mouseY <= 186)
    {
      if (count[id] > 0)
      {
        count[id]--;
      }
    }

    // Hvis musen er over reset-knappen nulstilles programmet.
    if (mouseX >= 534 && mouseY >= 121 && mouseX <= 594 && mouseY <= 146)
    {
      loop();
      count[id] = 0; // S\u00e6t variabel [id] i arrayen count til 0.
    }
  }

  public void display() //Skriver elementnavn og antal i det gr\u00e5 "molekyle" felt hvis count er over 0.
  {
    if (count[id] > 0)
    {
      weight = weight + molarweight * count[id];
      spcSpace = space;

      // Skriv elementnavnet indenfor den gr\u00e5 boks, med den specifikke afstand til de andre elementer.
      fill(20, 20, 20);
      textAlign(LEFT, BOTTOM);
      textSize(30);
      text(shortvers, 189 + spcSpace, 186);

      // Skriv count[id] i nederste h\u00f8jre hj\u00f8rne af navnet hvis count[id] er st\u00f8rre end 1.
      if (count[id] > 1)
      {
        textAlign(LEFT, BOTTOM);
        textSize(10);
        text(count[id], 189 + spcSpace + sw[id], 186);
      }

      space = space + sw[id] + 10; // Tilf\u00f8j sw[id] og et mellemrum p\u00e5 10px til den globale space variabel.
    }
  }
}
  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "Molberegner" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
