package jatek0001;

import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import jatek.Jatek;

import jxl.Workbook;
import jxl.write.Label;
import jxl.write.Number;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;

public class Jatek0001 implements Jatek {
	
	private static int tableWidth=500;
	
	private Jatek0001GameData jgd = new Jatek0001GameData();
	private Jatek0001Panel jk0001 = new Jatek0001Panel();
	
	@Override
	public boolean createSaveDataFormat(File save) {		
		if(!jgd.isToExcel()){
			jk0001.showMessage("toExcelProblem");
			return false;
		}
		try
	    {			
			WritableWorkbook w = Workbook.createWorkbook(save);
			WritableSheet s = w.createSheet(jgd.getName(), 0);
			s.addCell(new Label(0, 0, "Információk a játékról"));
			s.addCell(new Label(0, 3, "Kód"));s.addCell(new Label(1, 3, jgd.getCode()));
			s.addCell(new Label(0, 5, "Név"));s.addCell(new Label(1, 5, jgd.getName()));
			s.addCell(new Label(0, 7, "Szerző"));s.addCell(new Label(1, 7, jgd.getAuthor()));
			s.addCell(new Label(0, 9, "Dátum"));s.addCell(new Label(1, 9, jgd.getDate()));
			
			//input
			s.addCell(new Label(0,11,"Input mátrix"));
			
			for(int i = 0;i<jgd.getColLabels().length;i++){
				s.addCell(new Label(i+1,13,jgd.getColLabels()[i]));
			}
			for(int i = 0;i<jgd.getRowLabels().length;i++){
				s.addCell(new Label(0,14+i,jgd.getRowLabels()[i]));
			}
			for(int i = 0;i<jgd.getColLabels().length;i++){
				for(int j = 0;j<jgd.getRowLabels().length;j++){
					s.addCell(new Number(1+i,14+j,jgd.getData()[j][i]));
				}
			}
			//alfa-output
			int startAt=14+jgd.getRowLabels().length+2;
			double alfaMax=0.0;
			double divide=10.0;
			s.addCell(new Label(0,startAt-1,"Legjobb választás"));
			for(int i = 0 ;i<jgd.getBestChoice().length;i++){
				s.addCell(new Number(0,startAt+i,alfaMax/divide));
				s.addCell(new Number(1,startAt+i,jgd.getBestChoice()[i][0]));
				s.addCell(new Label(2,startAt+i,jgd.getRowLabels()[jgd.getBestChoice()[i][1].intValue()]));
				alfaMax++;
			}
			
			w.write();
			w.close();   
	    }
	    catch (WriteException e)
	    {
	      e.printStackTrace();
	      return false;
	    }
	    catch (IOException e)
	    {
	      e.printStackTrace();
	      return false;
	    }finally{
	    	
	    }
		
		return true;
	}

	@Override
	public void closeGame() {
		jk0001.getGameInputContent().setText("");
		jgd.setToExcel(false);
		jk0001.getGameResults().setText("A számított eredmények");
		jk0001.setPanel("input");
	}
	
	@Override
	public void loadData(Document d) {
		Element root = d.getDocumentElement();
		Element gameElement=null;
		NodeList children=null;
		getLabel(root,gameElement,children,"information");
		getLabel(root,gameElement,children,"name");
		getLabel(root,gameElement,children,"description");
		getLabel(root,gameElement,children,"date");
		
		//author
		String author="";
		children = root.getElementsByTagName("author");
		for (int childrenNum = 0; childrenNum < children.getLength(); childrenNum++){
			gameElement = (Element)children.item(childrenNum);
			author+=gameElement.getFirstChild().getNodeValue()+"<br>";
        }
		jgd.setAuthor(author);
		
		//inputs
		children = root.getElementsByTagName("collabels");
		gameElement = (Element)children.item(0);
		jgd.setColLabels(gameElement.getFirstChild().getNodeValue().split(","));
		
		children = root.getElementsByTagName("rowlabels");
		gameElement = (Element)children.item(0);
		jgd.setRowLabels(gameElement.getFirstChild().getNodeValue().split(","));
		
		children = root.getElementsByTagName("data");
		gameElement = (Element)children.item(0);
		
		String tempData = gameElement.getFirstChild().getNodeValue();
		tempData = tempData.replace("{", ",");
		tempData = tempData.replace("}", ",");
		
		String[] tempDataArray=tempData.split(",");
		
		int k=0;
		int j=0;
		int rows=jgd.getRowLabels().length;
		int cols=jgd.getColLabels().length;
		Double[][] data= new Double[rows][cols];
		for(int i=0;i<tempDataArray.length;i++){
			try{
				if(k!=cols){
					data[j][k]=Double.parseDouble(tempDataArray[i]);
					k++;
				}else{
					k=0;
					j++;
					data[j][k]=Double.parseDouble(tempDataArray[i]);
				}
			}catch(NumberFormatException e){
				e.getMessage();
			}catch(ArrayIndexOutOfBoundsException e){
				e.getMessage();
			}
		}
		jgd.setData(data);
		jgd.setGameInputHtmlContent("<html>"+createGameContent(jgd.getRowLabels(),jgd.getColLabels(),jgd.getData())+"</html>");
		jk0001.showMessage("loaded");
		
	}

	/**
	 * Az xml egy magadott tagjének tartalmát kérdezi le, és tárolja. 
	 * Egyelemű tagekre vonatkozik.
	 * @param root Element
	 * @param gameElement Element
	 * @param children NodeList
	 * @param elementName String
	 */
	private void getLabel(Element root,Element gameElement,NodeList children,String elementName){
		children = root.getElementsByTagName(elementName);
		gameElement = (Element)children.item(0);
		if(elementName.equals("information")) jgd.setCode(gameElement.getAttribute("code"));
	      else if(elementName.equals("name")) jgd.setName(gameElement.getFirstChild().getNodeValue());
	      else if(elementName.equals("description"))jgd.setDescription(gameElement.getFirstChild().getNodeValue());
	      else if(elementName.equals("date")) jgd.setDate(gameElement.getFirstChild().getNodeValue());
	}
	/**
	 * A kinyert adatokból, létrehozunk egy kiírható formát. Html tálázatban.
	 * @param rowL String[]
	 * @param colL String[]
	 * @param dt Double[]
	 * @return String
	 */
	private String createGameContent(String [] rowL, String[] colL, Double[][] dt){
		StringBuffer sb = new StringBuffer("<table border=1 width="+tableWidth+"><tr><td align=center>/</td>");
		for(int i=0;i<colL.length;i++){
			sb.append("<td>"+colL[i]+"</td>");
		}
		sb.append("</tr>");
		
		for(int i=0; i<rowL.length;i++){
			sb.append("<tr>");
			sb.append("<td>"+rowL[i]+"</td>");
			for(int j=0;j<colL.length;j++){
				sb.append("<td align=center>"+dt[i][j]+"</td>");
			}
			sb.append("</tr>");
		}
		sb.append("</table>");
		return sb.toString();
	}
	
	@Override
	public void playGame() {
		DecimalFormat df = new DecimalFormat("###.########");
		jgd.setMin(getMinPayOff());
		jgd.setMax(getMaxPayOff());
		
		jgd.setMinByRows(getMinsByRows());
		jgd.setMaxOfMinByRows(getMaxOfMinsByRows(jgd.getMinByRows()));
		jgd.setMaxPair(getMaxPair(jgd.getMinByRows()));
		
		jgd.setMaxByRows(getMaxsByRows());
		jgd.setMinOfMaxByRows(getMinsOfMaxsByRows(jgd.getMaxByRows()));
		jgd.setMinPair(getMinPair(jgd.getMaxByRows()));
		jgd.setRByCols(rByCols());
		jgd.setMaxByRowsR(getMaxByRowsR(jgd.getRByCols()));
		jgd.setMinPairR(getMinPair(jgd.getMaxByRowsR()));
		jgd.setMaxMaxPair(getMaxPair(jgd.getMaxByRows()));
		jgd.setAvgByRows(getAvgByRows());
		jgd.setAvgMaxPair(getMaxPair(jgd.getAvgByRows()));
		jgd.setBestChoice(getBestChoice(0.0,1.0,0.1));
		
		String minsByRows ="<table border=1>";
		for(int i = 0;i<jgd.getMinByRows().length;i++){
			minsByRows+="<tr><td>"+jgd.getRowLabels()[i]+"</td>"+"<td align=center>"+df.format(jgd.getMinByRows()[i])+"</td></tr>";
		}
		minsByRows+="</table>";
		
		String maxsByRows="<table border=1>";
		for(int i = 0;i<jgd.getMaxByRows().length;i++){
			maxsByRows+="<tr><td>"+jgd.getRowLabels()[i]+"</td>"+"<td align=center>"+df.format(jgd.getMaxByRows()[i])+"</td></tr>";
		}
		maxsByRows+="</table>";
		
		
		String maxsByRowsR="<table border=1>";
		for(int i = 0;i<jgd.getMaxByRowsR().length;i++){
			maxsByRowsR+="<tr><td>"+jgd.getRowLabels()[i]+"</td>"+"<td align=center>"+df.format(jgd.getMaxByRowsR()[i])+"</td></tr>";
		}
		maxsByRowsR+="</table>";
		
		String avgByRows="<table border=1>";
		for(int i = 0;i<jgd.getMaxByRowsR().length;i++){
			avgByRows+="<tr><td>"+jgd.getRowLabels()[i]+"</td>"+"<td align=center>"+df.format(jgd.getAvgByRows()[i])+"</td></tr>";
		}
		avgByRows+="</table>";
		
		String bestChoiceTable="<hr>Legjobb választás<hr><table border=1 width="+tableWidth+">";
		bestChoiceTable+="<tr><td>Alfa-érték</td><td>Legjobb választás értéke</td><td>Választás</tr>";
		double alfaMax=0.0;
		double divide=10.0;
		for(int i = 0 ;i<jgd.getBestChoice().length;i++){
			bestChoiceTable+="<tr><td align=center>"+df.format(alfaMax/divide)+"</td><td align=center>"+df.format(jgd.getBestChoice()[i][0])+"</td><td>"+jgd.getRowLabels()[jgd.getBestChoice()[i][1].intValue()]+"</tr>";
			alfaMax++;
		}
		bestChoiceTable+="</table>";
		
		StringBuffer sbResult=new StringBuffer();
		
		sbResult.append("<html><p>Eredmények</p>" +
				createGameContent(jgd.getRowLabels(),jgd.getColLabels(),jgd.getData()));
		sbResult.append("<hr><p>Minimax kritérium:<br>Választható alternatívák maximális értékei<br>");
		sbResult.append(maxsByRows);
		sbResult.append("<br><font color=#FF0000>Választás:"+jgd.getMinPair()+"</font></p>");
		
		sbResult.append("<hr><p>Maximin kritérium:<br>Választható alternatívák minimális értékei<br>");
		sbResult.append(minsByRows);
		sbResult.append("<br><font color=#FF0000>Választás:"+jgd.getMaxPair()+"</font>");
		
		sbResult.append("<hr><p>Minimax megbánás kritérium:<br>Befektetési alternatívák és társadalmi helyzet esetén a megbánás értékek");
		sbResult.append(createGameContent(jgd.getRowLabels(),jgd.getColLabels(),jgd.getRByCols()));
		sbResult.append("<br>Választható alternatívák maximális értékei<br>");
		sbResult.append(maxsByRowsR);
		sbResult.append("<br><font color=#FF0000>Választás:"+jgd.getMinPairR()+"</font>");
		
		sbResult.append("<hr><p>Maximax kritérium:<br>Választható alternatívák maximális értékei<br>");
		sbResult.append(maxsByRows);
		sbResult.append("<br><font color=#FF0000>Választás:"+jgd.getMaxMaxPair()+"</font>");
		
		sbResult.append("<hr><p>Nem elégséges ok kritérium:<br>Választható alternatívák átlagos értékei<br>");
		sbResult.append(avgByRows);
		sbResult.append("<br><font color=#FF0000>Választás:"+jgd.getAvgMaxPair()+"</font>");
		
		sbResult.append(bestChoiceTable);
		
		sbResult.append("<html>");
		
		jk0001.getGameResults().setText(sbResult.toString());
		jgd.setToExcel(true);
	}
	/**
	 * Legkisebb elem.
	 * @return Double egy Double elem.
	 */
	private Double getMinPayOff(){
		Double min=Double.POSITIVE_INFINITY;
		Double[][] data=jgd.getData();
		for(int i = 0 ;i<jgd.getRowLabels().length;i++){
			for (int j = 0; j<jgd.getColLabels().length;j++){
				if(data[i][j]<min){
					min=data[i][j];
				}
			}
		}
		return min;
	}
	/**
	 * Legnagyobb elem.
	 * @return Double egy Double elem
	 */
	private Double getMaxPayOff(){
		Double max=Double.NEGATIVE_INFINITY;
		Double[][] data=jgd.getData();
		for(int i = 0 ;i<jgd.getRowLabels().length;i++){
			for (int j = 0; j<jgd.getColLabels().length;j++){
				if(data[i][j]>max){
					max=data[i][j];
				}
			}
		}
		return max;
	}
	/**
	 * Legkisebb elemek kinyerése soronként.
	 * @return Double[] egy Double tömb.
	 */
	private Double[] getMinsByRows(){
		Double[][] data=jgd.getData();
		Double[] mins= new Double[jgd.getRowLabels().length];
		for(int i=0;i<mins.length;i++){
			mins[i]=Double.POSITIVE_INFINITY;
		}
		int k=-1;
		for(int i=0;i<jgd.getRowLabels().length;i++){
			k++;
			for(int j=0;j<jgd.getColLabels().length;j++){
				if(data[i][j]<mins[k]){
					mins[k]=data[i][j];
				}
			}
		}
		return mins;
	}
	/**
	 * A legnagyobb elem kinyerése a legkisebb sorelemekből.
	 * @param data Double[] egy Double tömb.
	 * @return Double
	 */
	private Double getMaxOfMinsByRows(Double[] data){
		Double maxMin=Double.NEGATIVE_INFINITY;
		for(int i=0;i<data.length;i++){
			if(data[i]>maxMin){
				maxMin=data[i];
			}
		}
		return maxMin;
	}
	/**
	 * A legnagyobb elem címkével.
	 * @param data Double[] egy Double tömb.
	 * @return String
	 */
	private String getMaxPair(Double[] data){
		String pair="{";
		Double max=Double.NEGATIVE_INFINITY;
		for(int i=0;i<data.length;i++){
			if(Double.compare(data[i], max)>0){
				max=data[i];
				pair="{{"+jgd.getRowLabels()[i]+","+max+"}}";
			}else if(Double.compare(data[i], max)==0){
				max=data[i];
				pair += ",{{"+jgd.getRowLabels()[i]+","+max+"}}";
			}
		}
		return pair;
	}
	/**
	 * A legnagyobb elemek soronként.
	 * @return Double[] egy Double tömb.
	 */
	private Double[] getMaxsByRows(){
		Double[][] data=jgd.getData();
		Double[] maxs= new Double[jgd.getRowLabels().length];
		for(int i=0;i<maxs.length;i++){
			maxs[i]=Double.NEGATIVE_INFINITY;
		}
		int k=-1;
		for(int i=0;i<jgd.getRowLabels().length;i++){
			k++;
			for(int j=0;j<jgd.getColLabels().length;j++){
				if(data[i][j]>maxs[k]){
					maxs[k]=data[i][j];
				}
			}
		}
		return maxs;
	}
	/**
	 * A legkisebb elemek a legnagyobb sorelemekből.
	 * @param data Double[] egy Double tömb.
	 * @return Double
	 */
	private Double getMinsOfMaxsByRows(Double[] data){
		Double minMax=Double.POSITIVE_INFINITY;
		for(int i=0;i<data.length;i++){
			if(data[i]<minMax){
				minMax=data[i];
			}
		}
		return minMax;
	}
	/**
	 * A legnagyobb sorelemekből a legkisebb, címkével.
	 * @param data Double[] egy Double tömb.
	 * @return String 
	 */
	private String getMinPair(Double[] data){
		String pair="";
		Double minMax=Double.POSITIVE_INFINITY;
		for(int i=0;i<data.length;i++){
			if(Double.compare(data[i], minMax)<0){
				minMax=data[i];
				pair="{"+jgd.getRowLabels()[i]+","+minMax+"";
			}else if(Double.compare(data[i], minMax)==0){
				minMax=data[i];
				pair += "},{"+jgd.getRowLabels()[i]+","+minMax+"}";
			}
		}
		return "{"+pair+"}";
	}
	/**
	 * Megbánás, oszloponként a maximum értékből kivonja az aktuális értéket.
	 * Létrejön egy új mátrix.
	 * @return Double[][] az új mátrixszal tér vissza.
	 */
	private Double[][] rByCols(){
		Double[][] r = new Double[jgd.getRowLabels().length][jgd.getColLabels().length];
		Double[][] data=jgd.getData();
		Double rMax=Double.NEGATIVE_INFINITY;
		for(int i = 0; i<jgd.getColLabels().length; i++){
			rMax=Double.NEGATIVE_INFINITY;
			for(int j = 0;j<jgd.getRowLabels().length;j++){
				if(data[j][i]>rMax){
					rMax=data[j][i];
				}
			}
			for(int k = 0; k<jgd.getRowLabels().length;k++){
				r[k][i]=rMax-data[k][i];
				
			}
		}
		return r;
	}
	/**
	 * A kapott megbánás mátrixból kinyeri soronként a maximum értéket.
	 * @param data Double[][]
	 * @return Double[] Tömbben adja vissza a soronkénti max értéket.
	 */
	private Double[] getMaxByRowsR(Double[][] data){
		Double[] maxs= new Double[jgd.getRowLabels().length];
		for(int i=0;i<maxs.length;i++){
			maxs[i]=Double.NEGATIVE_INFINITY;
		}
		int k=-1;
		for(int i=0;i<jgd.getRowLabels().length;i++){
			k++;
			for(int j=0;j<jgd.getColLabels().length;j++){
				if(data[i][j]>maxs[k]){
					maxs[k]=data[i][j];
				}
			}
		}
		return maxs;		
	}
	/**
	 * Sorátlagot számol.
	 * @return Double[] Tömbben adja vissza a sorátlagokat.
	 */
	private Double[] getAvgByRows(){
		Double[][] data=jgd.getData();
		Double[] avg=new Double[jgd.getRowLabels().length];
		for(int i = 0;i<jgd.getRowLabels().length;i++){
			avg[i]=0.0;
			for(int j = 0;j<jgd.getColLabels().length;j++){
				avg[i]+=data[i][j];
			}
			avg[i]/=jgd.getColLabels().length;
		}
		
		return avg;
	}
	/**
	 * Megadjuk a legjobb választást.
	 * @param from honnan
	 * @param to hova
	 * @param scale lépésköz
	 * @return Double[][] tartalmazza a választáshoz tartozó értéket és
	 * a hozzá tartozó címke sorszámát.
	 */
	private Double[][] getBestChoice(Double from,Double to,Double scale){
		Double[][] bestChoice=new Double[(int)(((to-from)/scale)+1)][2];
		Double[] maxs=jgd.getMaxByRows();
		Double[] mins=jgd.getMinByRows();
		Double scaleTmp=from;
		for(int k=0;k<bestChoice.length;k++){
			bestChoice[k][0]=Double.NEGATIVE_INFINITY;
			for(int i = 0;i<maxs.length;i++){				
				Double tmpMax=maxs[i]*(1-scaleTmp)+mins[i]*scaleTmp;
				if(Double.compare(tmpMax,bestChoice[k][0])>0){
					bestChoice[k][0]=tmpMax;					
					bestChoice[k][1]=(double)i;
				}
			}
			scaleTmp+=scale;
		}
		return bestChoice;	
	}
	
	public Jatek0001GameData getJgd() {
		return jgd;
	}
	public Jatek0001Panel getJk0001() {
		return jk0001;
	}
}
