// Analemmatic Sundial Computation
// Copyright 2008 by Richard Koolish

import java.util.*;
import java.math.*;
import java.text.*;

// java Analemmatic <lat> <lon> <semi-major>

public class Analemmatic {

    static DecimalFormat f = new DecimalFormat("00.000");

    static String[] months = {"Jan 1", "Feb 1", "Mar 1", "Apr 1", "May 1", "Jun 1",
                              "Jul 1", "Aug 1", "Sep 1", "Oct 1", "Nov 1", "Dec 1"};
    static Double[] sunDec = {-23.01, -17.33, -7.82, 4.30, 14.90, 21.97,
			      23.15, 18.16, 8.50, -2.95, -14.23, -21.71};

    public static void main (String[] args) {
	if (args.length < 3) {
	    System.out.println("\njava Analemmatic <lat> <lon> <semi-major>\n");
	    System.exit(0);
	}

	System.out.println("\nAnalemmatic Sundial computed by Richard Koolish\n");

	Double latD = Double.parseDouble(args[0]);
	Double lonD = Double.parseDouble(args[1]);
	Double M = Double.parseDouble(args[2]);
	Double latR = Math.toRadians(latD);
	Double lonR = Math.toRadians(lonD);
	Double m = M * Math.sin(latR);
	Double foc = Math.sqrt(M * M - m * m);
	Double zn = zone(lonD);
	Double zCorr = zn - lonD;
	System.out.println ("Lat: " + latD
			    + ", Lon: " + lonD
			    + ", Semi-Major: " + M
			    + ", Semi-Minor: " + f.format(m) 
			    + ", Focus: " + f.format(foc)
			    + "\n");
	
	System.out.println("Zone: " + zn
			   + ", Correction: " + f.format(zCorr) + " deg"
			   + " = " + f.format((zCorr / 15.0) * 60.0) + " min"
			   + "\n");

	System.out.println("Hour points as x and y, azimuth and radius ");
	System.out.println("or focus distances.\n");
	System.out.println("Dials not on the time zone meridian are adjusted ");
	System.out.println("for longitude and are not symmetrical.\n");

	for (int hr = 4; hr <= 20; hr++) {
	    Double haR = Math.toRadians((15.0 * (hr - 12)) - zCorr);
	    Double x = M * Math.sin(haR);
	    Double y = M * Math.sin(latR) * Math.cos(haR);
	    Double aR = Math.atan2(y, x);     // angle from horizontal 
	    Double aD = Math.toDegrees(aR);
	    Double z = Math.sqrt(x * x + y * y);
	    Double dx = x - foc;
	    Double dy = y;
	    Double f1 = Math.sqrt(dx * dx + dy * dy);
	    Double f2 = (2 * M) - f1;
	    System.out.println("Hour: " + hr
			       + ",   x: " + f.format(x)
			       + ", y: " + f.format(y)
			       + ",   az: " + f.format(90.0 - aD)
			       + ", z: " + f.format(z)
			       + ",   f1: " + f.format(f1)
			       + ", f2: " + f.format(f2)
			       );
	}

	System.out.println("\nDate offsets along the Y axis.\n");

	/* 
	for (Double decDx = -24.0; decDx < 25.0; decDx += 6.0) {
	    Double decD = decDx;
	    if (decD < -23.44)
		decD = -23.44;
	    if (decD > 23.44)
		decD = 23.44;
	    Double decR = Math.toRadians(decD);
	    Double s = M * Math.tan(decR) * Math.cos(latR);
	    System.out.println("Dec: " + f.format(decD) 
			       + ", s: " + f.format(s));
	}
	*/

	for (Double decDx = -24.0; decDx < 25.0; decDx += 6.0) { 
	    dateOffset (latR, M, "", decDx);
        }  

	System.out.println("\n");

	for (int i = 0; i < 12; i++) {
	    dateOffset (latR, M, months[i], sunDec[i]);
	}

	System.out.println("\n");

	dateOffset (latR, M, "Jun 21", 23.44);
	dateOffset (latR, M, "Dec 21", -23.44);

	System.out.println("\n");


    }


    public static void dateOffset (Double latR, Double M, String mon, Double decDx) {
	Double decD = decDx;
	if (decD < -23.44)
	    decD = -23.44;
	if (decD > 23.44)
	    decD = 23.44;
	Double decR = Math.toRadians(decD);
	Double s = M * Math.tan(decR) * Math.cos(latR);
	System.out.println(((mon != "") ? (mon + ",  ") : "")  
			   + "Dec: " + f.format(decD) 
			   + ",  Y Offset: " + f.format(s));
    }


    public static Double zone(Double lonD) {
	Double z = lonD / 15.0;
	long z1 = Math.round(z);

	/*
	System.out.println("lonD: " + lonD
			   + ", z: " + z
			   + ", z1: " + z1
			   );
	*/

	return z1 * 15.0;
    }
}
