// $Id: ContourMapReader.java,v 1.4.4.2 2006/03/11 20:08:12 wwxue Exp $

/*
* Copyright (c) 2006, The Hong Kong University of Science and Technology (HKUST)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without 
* modification, are permitted provided that the following conditions 
* are met:
*
*   * Redistributions of source code must retain the above copyright notice, 
*     this list of conditions,the authors and the following disclaimer.
*   * Redistributions in binary form must reproduce the above copyright notice,
*     this list of conditions,the authors and the following disclaimer in
*     the documentation and/or other materials provided with the distribution.
*   * Neither the name of the university nor the names of its 
*     contributors may be used to endorse or promote products derived from 
*     this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Wenwei Xue (wwxue@cs.ust.hk)
* Date last modified:  03/15/06
*/
package net.tinyos.tinydb;

import net.tinyos.util.ByteOps;

/**
 * Implements reader for CONTOURMAP aggregate
 *
 * @author Wenwei Xue
 * @version 0.1, March 15, 2006
 *
 */
public class ContourMapReader implements AggregateResultsReader {
    /**
	 * Reads data from the byte array from QueryResultMessage
	 */
    public void read(byte[] data) {
		
		int i,j;
		
		myState = new ContourMap();
		myState.regionNum = ByteOps.unsign(data[0]);			
		i=1;
	        for (j=0;j<myState.regionNum;j++)
	          {
	          	myState.regions[j].boundaries[0].vertices[0].x = ByteOps.unsign(data[i++]);
			myState.regions[j].boundaries[0].vertices[0].y = ByteOps.unsign(data[i++]);
			myState.regions[j].boundaries[0].vertices[1].x = ByteOps.unsign(data[i++]);
			myState.regions[j].boundaries[0].vertices[1].y = ByteOps.unsign(data[i++]);		
			myState.regions[j].regression.A[0] = ByteOps.makeSignedInt(data[i++], data[i++]);
			myState.regions[j].regression.A[1] = ByteOps.makeSignedInt(data[i++], data[i++]);
			myState.regions[j].regression.A[2] = ByteOps.makeSignedInt(data[i++], data[i++]);
			myState.regions[j].regression.A[3] = ByteOps.makeSignedInt(data[i++], data[i++]);
			myState.regions[j].regression.A[4] = ByteOps.makeSignedInt(data[i++], data[i++]);		
			myState.regions[j].regression.b[0] = ByteOps.makeSignedInt(data[i++], data[i++]);
			myState.regions[j].regression.b[1] = ByteOps.makeSignedInt(data[i++], data[i++]);
			myState.regions[j].regression.b[2] = ByteOps.makeSignedInt(data[i++], data[i++]);
			computeRegressionModel(j);
			myState.regions[j].epsilon = ByteOps.unsign(data[i++]) / 100;
		 }		
    }
	
	void computeRegressionModel (int j)
	  {
	    double d, d1, d2;
		double A [] = myState.regions[j].regression.A, b [] = myState.regions[j].regression.b, w [] = myState.regions[j].regression.w;
		
		d = 1*A[2] - A[0]*A[0];
		d1 = b[2]*d + b[0]*A[0]*A[3] - b[1]*1*A[3] - b[0]*A[2]*A[1] + b[1]*A[0]*A[1];
		d2 = A[0]*A[3]*A[1] - A[1]*A[2]*A[1] - 1*A[3]*A[3] + A[1]*A[0]*A[3] + A[4]*d;
		
		w[2] = d1;
		if (d2 != 0)
		  w[2] /= d2;
		else
		  w[2] = 0;
		  
		w[0] = w[2]*(A[0]*A[3] - A[1]*A[2]) + (b[0]*A[2] - b[1]*A[0]);
		if (d != 0)
		  w[0] /= d;
		else
		  w[0] = 0;
		  
		w[1] = w[2]*(1*A[3] - A[1]*A[0]) + (b[0]*A[0] - b[1]*1);
		if (d != 0)
		  w[1] /= (-d);
		else
		  w[1] = 0;
		  
		if (w[1] == -0)
		  w[1] = 0;			  	
	  }	
	
	/**
	 * Calculates final value of the aggregate.
	 */
	public void finalizeValue() {
		return;
	}
	
	/**
	 * Returns string representation of the finalized value
	 */
	public String getValue() {
	    printContourMap();
	    
	    return String.valueOf(myState.regionNum);
	}
	
	public void printContourMap() {
		String str = "#\r\n";
	    int i,j;	    
	    for (i=0;i<myState.regionNum;i++)
	      {
	      	str += "Contour Region #"+String.valueOf(i+1)+":"+"\r\n";
	      	str += "\r\n";
	      	for (j=0;j<MAX_BOUNDARY_NUM;j++)
	      	  {
	      	  	str += "Region Boundary #"+String.valueOf(j+1)+": ";
	      	  	str += "("+String.valueOf(myState.regions[i].boundaries[j].vertices[0].x)+","+String.valueOf(myState.regions[i].boundaries[j].vertices[0].y)+") ";               
	      	  	str += "("+String.valueOf(myState.regions[i].boundaries[j].vertices[0].x)+","+String.valueOf(myState.regions[i].boundaries[j].vertices[1].y)+") ";               
	      	  	str += "("+String.valueOf(myState.regions[i].boundaries[j].vertices[1].x)+","+String.valueOf(myState.regions[i].boundaries[j].vertices[1].y)+") ";               
	      	  	str += "("+String.valueOf(myState.regions[i].boundaries[j].vertices[1].x)+","+String.valueOf(myState.regions[i].boundaries[j].vertices[0].y)+") ";               
	      	  	str += "\r\n";    
	      	  }
	      	str += "\r\n";
	      	str += "Region Area: ";
	      	str += String.valueOf((Math.min(myState.regions[i].boundaries[0].vertices[0].x,myState.regions[i].boundaries[0].vertices[1].x) - Math.max(myState.regions[i].boundaries[0].vertices[0].x,myState.regions[i].boundaries[0].vertices[1].x)) 
	      	*(Math.min(myState.regions[i].boundaries[0].vertices[0].y,myState.regions[i].boundaries[0].vertices[1].x) - Math.max(myState.regions[i].boundaries[0].vertices[0].x,myState.regions[i].boundaries[0].vertices[1].y)));
	      	str += " (grid cell area: " + String.valueOf(Math.pow(GRID_CELL_LEN,2)) + ")";
	      	str += "\r\n";
	      	str += "\r\n";
	      	str += "Regression Model:" + "\r\n";
	      	str += "w = (";
	      	for (j=0;j<3;j++)
	      	  {
	      	  	str += String.valueOf(myState.regions[i].regression.w[j]);
	      	  	if (j!=2) str +=",";
	      	  	else str += ")" + "\r\n";
	      	  }
	      	str += "A = (";
	      	str += String.valueOf(1)+",";
	      	str += String.valueOf(myState.regions[i].regression.A[0])+",";
	      	str += String.valueOf(myState.regions[i].regression.A[1])+",";
	      	str += String.valueOf(myState.regions[i].regression.A[0])+",";
	      	str += String.valueOf(myState.regions[i].regression.A[2])+",";
	      	str += String.valueOf(myState.regions[i].regression.A[3])+",";
	      	str += String.valueOf(myState.regions[i].regression.A[1])+",";
	      	str += String.valueOf(myState.regions[i].regression.A[3])+",";
	      	str += String.valueOf(myState.regions[i].regression.A[4])+")" + "\r\n";
	      	str += "b = (";
	      	for (j=0;j<3;j++)
	      	  {
	      	  	str += String.valueOf(myState.regions[i].regression.b[j]);
	      	  	if (j!=2) str +=",";
	      	  	else str += ")" + "\r\n";
	      	  }
	      	str += "\r\n";	      	
	      	str += "Epsilon: " + String.valueOf(myState.regions[i].epsilon) + "\r\n";
	      	str += "\r\n";  
	      }
	    str += "Number of regions: " + String.valueOf(myState.regionNum) + "\r\n";
	    str += "#\r\n";
	    System.out.println(str);    
	 }
	
	public void copyResultState(AggregateResultsReader reader) {
		if (! (reader instanceof ContourMapReader)) throw new IllegalArgumentException("Wrong type reader");
		
		ContourMapReader other = (ContourMapReader)reader;
		other.myState = myState.copy();
		
	}
	
	ContourMap myState;
	
	static final int MAX_REGION_NUM = 4;
	static final int MAX_BOUNDARY_NUM = 1;
	static final int MAX_VERTEX_NUM = 2;
	static final int GRID_CELL_LEN = 20;
}

class Vertex {
	int x;	
	int y;
}

class ContourRegionBoundary {
		Vertex vertices [] = new Vertex [ContourMapReader.MAX_VERTEX_NUM];
		
		ContourRegionBoundary ()
		  {
		    for (int i=0;i<ContourMapReader.MAX_VERTEX_NUM;i++)
		      vertices[i] = new Vertex();		  	
		  }
		
		public ContourRegionBoundary copy ()
		  {
		  	ContourRegionBoundary boundary = new ContourRegionBoundary();
		  	
		  	for (int i=0;i<2;i++)
		  	  {
		  	  	boundary.vertices[i].x = this.vertices[i].x;
		  	  	boundary.vertices[i].y = this.vertices[i].y;
		  	  }
		  	  
		  	return boundary;		  	
		  }
} 

class RegressionModel {
		double w [] = new double [3];
		double A [] = new double [5];
		double b [] = new double [3];
		
		public RegressionModel copy ()
		  {
		  	RegressionModel regression = new RegressionModel();	
		  	
		  	for (int i=0;i<5;i++)
		  	  regression.A[i]=this.A[i];
		  	
		  	for (int i=0;i<3;i++)
		  	  {
		  	  	regression.b[i]=this.b[i];
		  	  	regression.w[i]=this.w[i];
		  	  }
		  	  
		  	return regression;	  	
		  }
} 

class ContourRegion {
	ContourRegionBoundary boundaries [] = new ContourRegionBoundary [ContourMapReader.MAX_BOUNDARY_NUM];
	RegressionModel regression;
	double epsilon;
	
	ContourRegion ()
	  {
	  	for (int i=0;i<ContourMapReader.MAX_BOUNDARY_NUM;i++)
	  	  boundaries[i] = new ContourRegionBoundary();
	  	  
	  	regression = new RegressionModel();	
	  }
	
	public ContourRegion copy ()
	  {
	  	ContourRegion region = new ContourRegion ();
	  	
	  	for (int i=0;i<1;i++)
	  	  region.boundaries[i] = this.boundaries[i].copy();
	  	region.regression = this.regression.copy();
	  	region.epsilon = this.epsilon;
	  	
	  	return region;	  	
	  }
}

class ContourMap  {
	ContourRegion regions [] = new ContourRegion [ContourMapReader.MAX_REGION_NUM];
	int regionNum;
	
	ContourMap ()
	  {
	  	for (int i=0;i<ContourMapReader.MAX_REGION_NUM;i++)
	  	  regions[i] = new ContourRegion();	
	  }
	
	public ContourMap copy ()
	  {
	  	ContourMap map = new ContourMap();
	  	
	  	map.regionNum = this.regionNum;
	  	for (int i=0;i<4;i++)
	  	  map.regions[i]=this.regions[i].copy();
	  	
	  	return map;
	  }
}
