package hh.michelson;

import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

import hh.lib.D;
import hh.lib.DA;

enum Wave {
	SINE {
		@Override
		double value(double x) {
			return Math.sin(x);
		}
	},
	SAW {
		@Override
		double value(double x) {
			return x / C.PI - 1;
		}
	},
	TRIAN {
		@Override
		double value(double x) {
			return (x < C.PI ? 2*x/ C.PI - 1 : 2*(1 - x/ C.PI) + 1);
		}
	},
	SQUARE {
		@Override
		double value(double x) {
			return x < C.PI ? 1 : -1;
		}
	},
	F1 {
		final HandleDef[] F1Def = new HandleDef[] {
				new HandleDef(name() + "K00", "N", 1, 30, 3),
				new HandleDef(name() + "K01", "K", 0, 2, 1)
		};

		@Override
		public String toString() {
			return "SUM(N) sin(N * x) / pow(N, K)";
		}

		@Override
		void setHandleDef() { paramsDef = F1Def; }

		@Override
		void adjustParams() {
			n = (int)Math.round(params[0]);
			k1 = new double[n];
			for(int i = 1; i < n; i += 2) k1[i] = Math.pow(i, params[1]);
		}

		@Override
		double value(double x) {
			double y = 0;
			for(int i = 1; i < n; i += 2) y += Math.sin(i * x) / k1[i];
			return y;
		}
	},
	F2 {
		@Override public String toString () { return "sin(x)+cos(x) + (sin(2x)+cos(2x))/2 + (sin(4x)+cos(4x))/4"; }

		@Override
		double value(double x) {
			return (Math.sin(x) + Math.cos(x) + 0.5 * Math.sin(2*x) + 0.5 * Math.cos(2*x) +
					0.25 * Math.sin(4*x) + 0.25 * Math.cos(4*x));
		}
	},
	F3 {
		@Override
		public String toString () { return "cos(ln(x))"; }

		@Override
		double value(double x) { return Math.cos(Math.log(x)); }
	},
	F4 {
		@Override public String toString () { return "cos(x^3))"; }

		@Override
		double value(double x) { return Math.cos(Math.pow(x, 3)); }
	},
	F5 {
		@Override public String toString () { return "cos(sin(x)))"; }

		@Override
		double value(double x) { return Math.cos(Math.sin(x)); }
	},
	F6 {
		@Override public String toString () { return "sin(cos(x)))"; }

		@Override
		double value(double x) { return Math.sin(Math.cos(x)); }
	},
	F7 {
		@Override public String toString () { return "sin(sin(x)))"; }

		@Override
		double value(double x) { return Math.sin(Math.sin(x)); }
	},
	F8 {
		@Override public String toString () { return "cos(cos(x)))"; }

		@Override
		double value(double x) { return Math.cos(Math.cos(x)); }
	},
	F9 {
		@Override public String toString () { return "ln(sin(x))"; }

		@Override
		double value(double x) { return Math.log(Math.sin(x)); }
	},
	F10 {
		@Override public String toString () { return "tan(sin(x)+cos(x))"; }

		@Override
		double value(double x) { return Math.tan(Math.sin(x) + Math.cos(x)); }
	},
	MICHELSON {
		final int numOfWaves = 20;

		@Override public String toString () { return "SUMn (Kn * sin(n*x))"; }

		@Override
		void setHandleDef() {
			paramsDef = new HandleDef[numOfWaves];
			for(int i = 0; i<paramsDef.length; i++) paramsDef[i] = new HandleDef(
					String.format("%sK%02d", Wave.MICHELSON.name(), i), String.format("%02d", i+1), -10, 10, 0);
		}

		@Override
		double value(double x) {
			double y = 0;
			for (int i = 0; i < params.length; i++) y += params[i] * Math.sin((i+1) * x);
			return y;
		}
	};

	static String[] names, titles;

	static {
		final List<String> t = new ArrayList<>();
		final List<String> n = new ArrayList<>();
		for (Wave w : Wave.values()) { t.add(w.toString()); n.add(w.name()); }
		titles = new String[t.size()];
		t.toArray(titles);
		names = new String[n.size()];
		n.toArray(names);
	}

	class ParamHandle extends Handle implements SeekBar.OnSeekBarChangeListener {

		ParamHandle(D d, int i) { super(d, paramsDef[i], params[i]); }

		@Override
		public void onProgressChanged(SeekBar bar, int progress, boolean fromUser) {
			super.onProgressChanged(bar, progress, fromUser);
			if(fromUser) {
				synchronized(context) {
					setParam(listPosition, actual);
					adjust();
				}
				graphView.invalidate();
			}
		}

		@Override
		public boolean onLongClick(View v) {
			final boolean r = super.onLongClick(v);
			synchronized(context) {
				setParam(listPosition, actual);
				adjust();
			}
			graphView.invalidate();
			return r;
		}

		@Override
		void infillView(View handleView, int listPosition) {
			super.infillView(handleView, listPosition);
			labelView.setOnLongClickListener(this);
			bar.setOnSeekBarChangeListener(this);
		}
	}

	String tag = getClass().getName();

	D d;
	A context;
	int n;
	double k1[];
	double[] params = null;
	HandleDef[] paramsDef = null;
	ParamHandle[] paramHandles;
	RotatedLv paramView;

	GraphView graphView;
	float[] graphValues;
	double maxY = 0d;

	double value(double x) {
		return 0.0d;
	}

	boolean hasParams() { return params != null; }

	void init(A context) {
		this.context = context;
		this.d = context.d.klon(this);
		((TextView)context.findViewById(R.id.dispWave)).setText(String.format("%s", this));
		paramView = context.findViewById(R.id.params);
		graphView = context.findViewById(R.id.graph);
		graphValues = new float[(int)Math.ceil(A.rs.getDimension(R.dimen.graph_width))];
        /*graphValues = new double[1 + (int)Math.ceil(2 * D.SR / freq)];*/
		graphView.setGraphValues(graphValues);
		initParams();
		adjust();
		setParamsView();
		graphView.invalidate();
	}

	void adjust() {
		adjustParams();
		computeWaveGraph();
	}

	void setHandleDef() {}

	void initParams() {
		if(params == null) {
			setHandleDef();
			if(paramsDef != null) {
				params = new double[paramsDef.length];
				for(int i = 0; i < params.length; i++)
					params[i] = DA.sp.getFloat(paramsDef[i].Key, Double.valueOf(paramsDef[i].Default).floatValue());
			}
		}
	}

	void adjustParams() {}  // předzpracování parametrů vlny po změně nastavitelného parametru

	void setParam(int i, double value) { params[i] = value; }

	void resetParams() {
		if(hasParams()) {
			for(int i=0; i < params.length; i++) paramHandles[i].reset();
			synchronized(context) {
				for(int i = 0; i < params.length; i++) setParam(i, paramsDef[i].Default);
				adjust();
			}
			graphView.invalidate();
		}
	}

	void setParamsView() {
		final View reset = context.findViewById(R.id.reset);
		if(hasParams()) {
			reset.setVisibility(View.VISIBLE);
			reset.setOnClickListener(new View.OnClickListener() {
				public void onClick(View v) {
					resetParams();
				}
			});
			setParamHandles();
			paramView.setAdapter(new Windrover(d, "Params", context, R.layout.wave_params_row, paramHandles));
		}
		else {
			reset.setVisibility(View.GONE);
			paramView.setAdapter(null);
		}
	}

	void setParamHandles() {
		paramHandles = new ParamHandle[params.length];
		for(int i = 0; i < params.length; i++) { paramHandles[i] = new ParamHandle(d, i); }
	}

	void computeWaveGraph() {
		final double  dt = 2 * C.twoPI / graphValues.length;       /*dt = D.twoPI * freq / D.SR,*/
		double  t = 0d;
		maxY = 0d;
		for (int i=0; i<graphValues.length; i++) {
			final double v = value(t % C.twoPI);
			if(Math.abs(v) > maxY) maxY = Math.abs(v);
			graphValues[i] = Double.valueOf(v).floatValue();
			t += dt;
		}
		graphView.setMaxY(maxY);
		context.setMaxY(maxY);
	}
}
