package lt.ktu.gmj.tasks;

import java.util.Random;
import lt.ktu.gmj.AbstractTask;
import lt.ktu.gmj.Domain;
import lt.ktu.gmj.DomainWithConstraint;
import lt.ktu.gmj.Point;
import lt.ktu.gmj.TaskWithAnalyzers;
import lt.ktu.gmj.propertySheet.FieldProvider;
import lt.ktu.gmj.propertySheet.IntProperty;
import lt.ktu.gmj.propertySheet.PropertyManager;

class ModelDomain extends DomainWithConstraint {
	static final String dimensions[] = { "gmin1", "gmax1", "a1", "v1", "gmin2",
			"gmax2", "a2", "v2" };

	ModelDomain() {
		min[0] = 0.001;		max[0] = 0.5;
		min[1] = 0.5;		max[1] = 8;
		min[2] = 0.01;		max[2] = 0.1;
		min[3] = -10;		max[3] = 80;
		min[4] = 0.001;		max[4] = 0.5;
		min[5] = 0.5;		max[5] = 8;
		min[6] = 0.01;		max[6] = 0.1;
		min[7] = -10;		max[7] = 80;
		
		defaultPoint.x[0]=min[0];
		defaultPoint.x[1]=min[1];
		defaultPoint.x[2]=min[2];
		defaultPoint.x[3]=min[3];
		defaultPoint.x[4]=min[4];
		defaultPoint.x[5]=min[5];
		defaultPoint.x[6]=min[6];
		defaultPoint.x[7]=min[7];
	}

	public String[] dimensions() {
		return dimensions;
	}
}


public class Model 
{
	private ChannelProperties properties = new ChannelProperties();
	
	public double[][] Run()
	{	
		Protocol protocol = new Protocol();		
		Channel channels[] = new Channel[properties.getX()];
		for (int i = 0; i < channels.length; i++) {
			channels[i] = new Channel(properties);
		}

		double g1[] = new double[protocol.getT()];
		double v1[] = new double[protocol.getT()];
		double g2[] = new double[protocol.getT()];
		double v2[] = new double[protocol.getT()];

		double v;

		for (int i = 0; i < protocol.getTs0(); i++) {
			double g=0;
			for (int j = 0; j < channels.length; j++) {
				channels[j].Switch(protocol.getV0(), properties.getA(),
						properties.getB());
				g+=channels[j].getG();
			}
		}

		v = protocol.getV0();
		for (int i = 0; i < protocol.getT(); i++) {
			double g = 0;
			for (int j = 0; j < channels.length; j++) {
				channels[j].Switch(v, properties.getA(), properties.getB());
				g += channels[j].getG();
			}
			g1[i] = g / channels.length;
			v1[i] = v;

			v += protocol.getV();
		}

		for (int i = 0; i < protocol.getTs1(); i++) {
			double g=0;
			for (int j = 0; j < channels.length; j++) {
				channels[j].Switch(protocol.getV0(), properties.getA(),
						properties.getB());
				g+=channels[j].getG();
			}			
		}

		v = protocol.getV0();
		for (int i = 0; i < protocol.getT(); i++) {
			double g = 0;
			for (int j = 0; j < channels.length; j++) {
				channels[j].Switch(v, properties.getA(), properties.getB());
				g += channels[j].getG();
			}
		
			g2[i] = g / channels.length;
			v2[i] = v;

			v -= protocol.getV();
		}

		for (int i = 0; i < protocol.getTs0(); i++) {
			double g=0;
			for (int j = 0; j < channels.length; j++) {
				channels[j].Switch(protocol.getV0(), properties.getA(),
						properties.getB());
				g+=channels[j].getG();
			}		
		}

		double result[][]=new double[2][];
		result[0]=new double[protocol.getT()*2];
		result[1]=new double[protocol.getT()*2];

		int index = 0;
		for (int i = v2.length - 1; i >= 0; i--) {
			result[0][index] = v2[i];
			result[1][index] = g2[i];
			index++;
		}

		for (int i = 0; i < v1.length; i++) {
			result[0][index] = v1[i];
			result[1][index] = g1[i];
			index++;
		}
		
		return result;
	}
	
	public ChannelProperties getProperties() {
		return properties;
	}

	public void setProperties(ChannelProperties properties) {
		this.properties = properties;
	}
}

class Gate {
	private static Random random = new Random();

	private boolean state = true;
	private double g = 0;
	private double v = 0;

	public boolean getState() {
		return state;
	}

	public double getG() {
		return g;
	}

	public double getV() {
		return v;
	}

	public void setV(double value) {
		v = value;
	}

	public void Switch(double v, GateProperties properties) {

		double k = Math.exp(properties.getA() * ((properties.getP() * this.v)- properties.getV0()));
		double po, pc;

		if (state) {
			pc = (properties.getK() * k) / (1 + k);
			po = 1 - pc;
		} else {
			po = properties.getK() / (1 + k);
			pc = 1 - po;
		}

		double p = random.nextDouble();
		if (p <= po) {
			state = true;
			g = properties.getGo();
		} else {
			state = false;
			g = properties.getGc();
		}
	}

	public void SwitchToOpen(double v, GateProperties properties) {
		state = true;
		g = properties.getGo();
	}
}

class Channel {
	private Gate a = new Gate();
	private Gate b = new Gate();
	private double g = 0;

	public Channel(ChannelProperties properties) {
		a.SwitchToOpen(0, properties.getA());
		b.SwitchToOpen(0, properties.getB());

		g = a.getG() * b.getG() / (a.getG() + b.getG());
	}

	public Gate getA() {
		return a;
	}

	public Gate getB() {
		return b;
	}

	public double getG() {
		return g;
	}

	public void Switch(double v, GateProperties aProperties,
			GateProperties bProperties) {

		a.setV(v * g / a.getG());
		b.setV(v * g / b.getG());

		a.Switch(v, aProperties);
		b.Switch(v, bProperties);

		g = a.getG() * b.getG() / (a.getG() + b.getG());
	}
}

enum Poliarity {
	Positive, Negative;

	public int getValue() {
		switch (this) {
		case Positive:
			return 1;
		case Negative:
			return -1;
		}

		return 0;
	}
}

class GateProperties {
	private double go = 2;
	private double gc = 0.2;
	private double a = 0.1;
	private double v0 = 40;
	private Poliarity poliarity = Poliarity.Positive;
	private double k = 0.1;

	public GateProperties(Poliarity p) {
		poliarity = p;
	}

	public double getGo() {
		return go;
	}

	public void setGo(double value) {
		go = value;
	}

	public double getGc() {
		return gc;
	}

	public void setGc(double value) {
		gc = value;
	}

	public double getA() {
		return a;
	}

	public void setA(double value) {
		a = value;
	}

	public double getV0() {
		return v0;
	}

	public void setV0(double value) {
		v0 = value;
	}

	public double getK() {
		return k;
	}

	public void setK(double k) {
		this.k = k;
	}

	public int getP() {
		return poliarity.getValue();
	}

	public Poliarity getPoliarity() {
		return poliarity;
	}

	public void setPoliarity(Poliarity p) {
		poliarity = p;
	}
}

class ChannelProperties {
	private GateProperties a = new GateProperties(Poliarity.Positive);
	private GateProperties b = new GateProperties(Poliarity.Negative);
	private int x = 1000;

	public GateProperties getA() {
		return a;
	}

	public GateProperties getB() {
		return b;
	}

	public int getX() {
		return x;
	}

	public void setX(int value) {
		x = value;
	}
}

class Protocol {
	private int t = 1200;
	private int ts0 = 100;
	private int ts1 = 1000;

	private double v = 0.1;
	private double v0 = 0;

	public int getT() {
		return t;
	}

	public void setT(int t) {
		this.t = t;
	}

	public int getTs0() {
		return ts0;
	}

	public void setTs0(int ts0) {
		this.ts0 = ts0;
	}

	public int getTs1() {
		return ts1;
	}

	public void setTs1(int ts1) {
		this.ts1 = ts1;
	}

	public double getV() {
		return v;
	}

	public void setV(double v) {
		this.v = v;
	}

	public double getV0() {
		return v0;
	}

	public void setV0(double v0) {
		this.v0 = v0;
	}
}