
import java.applet.Applet;
import java.awt.*;
import java.io.*;
import java.net.URL;
import java.util.*;
// import java.lang.Math;

// -----------------------------------------------------------------------   
class vector
 { double x,y,z;
   vector(double theta, double phi)
     { x = Math.cos(theta) * Math.cos(phi);
       y = Math.cos(theta) * Math.sin(phi);
       z = Math.sin(theta);
     }
   public vector(double lax, double lay, double laz)
     { x=lax;
       y=lay;
       z=laz;
     }

   vector()
    {x=0; y=0; z=0;}

   double theta() {return Math.atan2(this.z, 
                                  Math.sqrt(this.x*this.x + this.y*this.y));}
   double phi() {return Math.atan2(this.y,this.x);}
   vector out(vector v){ return new vector(  this.y * v.z - this.z * v.y,
                                           - this.x * v.z + this.z * v.x,
                                             this.x * v.y - this.y * v.x
                                           ); }
   vector times(double d)
       { return new vector( this.x * d, this.y * d, this.z *d); }
   vector min(vector v) 
       { return new vector( this.x - v.x, this.y -v.y, this.z - v.z); }
   vector plus(vector v)
       { return new vector( this.x + v.x, this.y + v.y, this.z + v.z);}
   double inner(vector v){ return this.x*v.x + this.y*v.y + this.z * v.z;}                           
   vector div(double d){ return new vector( this.x/d, this.y/d, this.z/d);}
   double len()
       { return Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z);}
 };
// -----------------------------------------------------------------------
class location
 { String en,eo,ne,pl;  // name in 4 languages
   double x,y;       // position
   location next;    // for linked list
   
   double sign(int i) { return i==0 ? 0.0 : i>0 ? 1.0 : -1.0; }
   
   location(String s1, int tx, int txm, int ty, int tym)
      { en=s1; eo=s1; ne=s1; pl=s1;
        x=tx+sign(tx)*Math.abs((double)(100*txm/60))/100; 
        y=ty+sign(ty)*Math.abs((double)(100*tym/60))/100;
        next=null;
      }  
   location(String s1, double tx, double ty)
      { en=s1; eo=s1; ne=s1; pl=s1;
        x=tx; y=ty;
        next=null;
      }
   location(String s1, String s2, String s3, String s4,  int tx, int txm, int ty, int tym)
      { en=s1; eo=s2; ne=s3; pl=s4;
        x=tx+sign(tx)*Math.abs((double)(100*txm/60))/100; 
        y=ty+sign(ty)*Math.abs((double)(100*tym/60))/100;
        next=null;
      }
   location(String s1, String s2, String s3, String s4,  double tx, double ty)
     { en=s1; eo=s2; ne=s3; pl=s4;
       x=tx; y= ty;
       next=null;  
     }    
   String name(int lang) {switch(lang) {case 1: return en;
                                        case 2: return eo;
                                        case 3: return ne;
                                        case 4: return pl;
                                       }
                          return en;
                         }              
 };

// ---------------------------------------------------------------------
class locationlist
  { location base;
    location current;
    int size;
    locationlist(){base=null; current=null; size=0;}
    void add(location a)
     { if(base==null) {base=a; current=base;}
       else { current.next=a; current=current.next;}
       size++;
     }   
    void sort(int lang) // n^2 method, may be Quicksort is possible.
     { location test,hulp,i;
       if(base==null) return;
       test=base.next;
       base.next=null;
       while(test!=null)
          { if(test.name(lang).compareTo(base.name(lang))<0)
                 {hulp = test.next;
                  test.next = base;
                  base = test;
                  test = hulp;
                 }
            else {i=base;
                  while(i.next!=null && 
                        test.name(lang).compareTo(i.next.name(lang))>0
                        ) i=i.next;    
                  hulp=test.next;
                  test.next=i.next;
                  i.next=test;
                  test=hulp;
                 }
           } 
     } // sort         
    protected void finalize() throws Throwable
    { location hulp;
      while(base!=null){ hulp=base; base=null; base=hulp.next;}
    }  // hmm, vast niet nodig.
  };   
// ------------------------------------------------------------------------
// ------------------------------------------------------------------------  
public class mekka extends Applet 
{ 
/*  overrided methods: 
    start(), init(), paint(), update(), action(), mouseDown(), mouseMove(),
    main(), getAppletInfo
*/

public String getAppletInfo()
{ switch(language){
    default: return "`Mecca' program \n august 1997 \n Michiel Meeuwissen " +
              " \n You may use and copy this program freely but please " +
              "let me know";
              }
}      

public String[][] getParameterInfo()         
{ String[][] en = {{"language","english/esperanto/nederlands/polski",
                      "the language used"},
                     {"locations","<location> <x> [min] <y> [min] [ , <location2> ...",
                      "Gives extra locations"}
                    };

  switch(language){
    default: return  en;    
                  }  
}                       
// variables:
public static double Degree=0.01745329251994;
public static double Rad=57.29477951308;
Label fromlabel, tolabel, Nfromlabel, Efromlabel, Ntolabel, Etolabel;
TextField tffromx, tffromy, tftox,tftoy;
Button teken, clear;
Choice froms, tos;
String worldfile=null;
int language=0;
locationlist ll=new locationlist();
Color blauw,grijs, textkl;
public static final int english=1;
public static final int esperanto=2;
public static final int nederlands=3;
public static final int polski=4;

String[][] texts= { {"from","de", "van","z"},
                    {"to",  "al", "naar","do"},
                    {" degrees"," gradoj", " graden"," stopnie"},
                    {" from east"," de oriento"," van het oosten"," zachodu"},
                    {"starting direction: ","ekirdirekto: ","startrichting: ","kierunek pocz\u0105tkowy: "},
                    {"distance: ","distanco: ", "afstand: ","dystans: "},
                    {"mouse: ", "muso: ", "muis: ","myszka: "},
                    {"draw","eku","teken", "podgl\u0105d"},
                    {"Mecca","Mekko","Mekka","Mekka"},
                    {"clear","visxu","schoon","wyczy\u015b"}
                  };
    

public String text(int n)
  { return texts[n-1][language-1];
  }             
               
 
static double alfa(vector S, vector D)
  { vector T,north,east;
    vector N= new vector(0,0,1);
    double y,x;
    T = (S.out(D)).out(S);
    north = (S.out(N)).out(S);
    north = north.div(north.len());
    east  = north.out(S);
    y = T.inner(north);
    x = T.inner(east);
    return Math.atan2(y,x);
   };


public void start()
  {//blauw = new Color(0x00,0x50,0x00); // meer groen nu
   blauw = getBackground();
   //grijs = new Color(0xb0,0xb0,0xb0);
   grijs = getBackground();
   textkl = Color.black;
   // setBackground(blauw);
   // init();
  }

public void init()
  { String s, locations;
    worldfile = getParameter("worldfile");
    if (worldfile == null) worldfile = "world.dat";
    s = getParameter("language");
    if (s == null) language = english;
           else if (s.equalsIgnoreCase("english")) language=english;
           else if (s.equalsIgnoreCase("esperanto")) language=esperanto;
           else if (s.equalsIgnoreCase("nederlands")) language=nederlands;
           else if (s.equalsIgnoreCase("polski")) language=polski;
           else language = esperanto;
    
    locations = getParameter("locations");
    StringTokenizer t = new StringTokenizer(locations," "); 
    String hulp1, hulp2, hulp3, hulp4, hulp5;
    try{
    while(t.hasMoreTokens())   
        { hulp1=t.nextToken();
          hulp2=t.nextToken();
          hulp3=t.nextToken();
          if(t.hasMoreTokens()) hulp4=t.nextToken();
                else hulp4="*";
          if (hulp4.compareTo(",")!=0 && hulp4.compareTo("*")!=0) 
                          { hulp5=t.nextToken();  
                            ll.add(new location(
                                   hulp1, Integer.valueOf(hulp2).intValue(),
                                          Integer.valueOf(hulp3).intValue(),
                                          Integer.valueOf(hulp4).intValue(),
                                          Integer.valueOf(hulp5).intValue()
                                   ));
                            if(t.hasMoreTokens()) t.nextToken();       
                           }
                    else  { ll.add(new location(
                                   hulp1, Double.valueOf(hulp2).doubleValue(),
                                          Double.valueOf(hulp3).doubleValue()
                                  ));
                          }        
         }// while
        }// try
       catch(NoSuchElementException e){} 
       catch(NumberFormatException e){}
               
    setLayout(null);
    // addNotify();

    fromlabel= new Label(text(1)+": ", Label.RIGHT);
    add(fromlabel);    
    fromlabel.reshape(insets().left+0, insets().top + (int)(factor*180+30),50,32);

    tffromx = new TextField("0",7);
    add(tffromx);
    tffromx.reshape(insets().left+50, insets().top + (int)(factor*180+30),60,32);
    
    Efromlabel = new Label("\u00b0E", Label.LEFT);
    add(Efromlabel);
    Efromlabel.reshape(insets().left+110, insets().top +(int)(factor*180+30),30,32);
     
    tffromy = new TextField("0",7);
    add(tffromy);
    tffromy.reshape(insets().left+140, insets().top + (int)(factor*180+30),60,32);
    
    Nfromlabel =new Label("\u00b0N", Label.LEFT);
    add(Nfromlabel);
    Nfromlabel.reshape(insets().left+200,insets().top  + (int)(factor*180+30),30,32);
    
    location il;
    ll.add(new location("Utrecht","Utrehxto","Utrecht","Utrecht",5.12,52.10));
    ll.add(new location("Mecca","Mekko","Mekka","Mekka",39.97,21.5));
    ll.add(new location("New York","Nov-Jorko","New York","Nowy Jork",-74,0,40,45));
    ll.add(new location("Vancouver","Vankuvero","Vancouver", "Vancouver",-123,8,49,18));
    ll.add(new location("Paris","Parizo","Parijs","Pary\u017c",2,20,48,52));
    ll.add(new location("Buenos Aires","Buenos-Ajreso","Buenos Aires","Buenos Aires", -58,-30,-34,-40));
    ll.add(new location("Moscow","Moskvo","Moskou","Moskwa", 37,42,55,45));
    ll.add(new location("Istanbul","Istanbulo","Istanboel", "Istanbu\u0142",28,57,41,02));
    ll.add(new location("Cairo","Kairo","Cairo","Kair", 31,15,30,03));
    ll.add(new location("Cape town","Kab-urbo","Kaapstad","Kapsztad", 18,28,-33,-56));
    ll.add(new location("Bombay","Bombajo","Bombay","Bombaj" ,72,51,18,56));
    ll.add(new location("Beijing","Pekino","Peking","Pekin", 116,30,40,00));
    ll.add(new location("Hong Kong","Hongkongo","Hong Kong","Hong Kong", 113,55,22,20));
    ll.add(new location("Jakarta","Gxakarto","Jakarta","D\u017cakarta", 106,45,-6,-8));
    ll.add(new location("Sydney","Sidnejo","Sydney","Sydney", 151,10,-33,-55));
    ll.add(new location("Bogota","Bogoto","Bogota","Bogota", -74,-30,4,30));
    ll.add(new location("Kinshasa","Kinsxaso","Kinshasa","Kinshasa", 15,18,-4,-18));    
    ll.add(new location("Tokio","Tokio","Tokio","Tokio", 139, 45,35,43));    
    ll.sort(language);
    
    
    froms = new Choice();
    froms.addItem("-");
    il=ll.base;
    while(il!=null) {froms.addItem(il.name(language)); il=il.next;}    
    add(froms);
    froms.reshape(insets().left+240,insets().top + (int)(factor*180+30),120,20);
    
    
    tolabel= new Label(text(2)+": ", Label.RIGHT);
    add(tolabel);
    tolabel.reshape(insets().left+0, insets().top + (int)(factor*180+60),50,32);

    tftox = new TextField("0",7);
    add(tftox);
    tftox.reshape(insets().left+50, insets().top + (int)(factor*180+60),60,32);

    Etolabel = new Label("\u00b0E", Label.LEFT);
    add(Etolabel);
    Etolabel.reshape(insets().left+110, insets().top +(int)(factor*180+60),30,32);

    
    tftoy = new TextField("0",7);
    add(tftoy);
    tftoy.reshape(insets().left+140, insets().top + (int)(factor*180+60),60,32);
    
    Ntolabel =new Label("\u00b0N", Label.LEFT);
    add(Ntolabel);
    Ntolabel.reshape(insets().left+200,insets().top  + (int)(factor*180+60),30,32);
    
    tos = new Choice();
    tos.addItem("-");
    il=ll.base;
    while(il!=null) {tos.addItem(il.name(language)); il=il.next;}    
    add(tos);
    tos.reshape(insets().left+240,insets().top + (int)(factor*180+60),120,20);
    
    
    teken = new Button(text(8));
    add(teken);
    teken.reshape(insets().left+390,insets().top + (int)(factor*180+30),70,20);
    
    clear = new Button(text(10));
    add(clear);
    clear.reshape(insets().left+470,insets().top + (int)(factor*180+30),70,20);
    
  }// init

               
               
public boolean action(Event e, Object arg)
  { Object target=e.target;
    if (target==teken) { 
                         drawfromto=true; 
                         repaint();
                       }
    if (target==clear) { from=true; 
                         drawfromto=false; 
                         redraw=true; 
                         repaint();}
    location il;
    if (target instanceof Choice) 
                       { if (target==froms)                           
                            { il=ll.base;
                              while(il!=null)
                                { if(arg==il.name(language))
                                     { tffromx.setText(""+il.x);
                                       tffromy.setText(""+il.y);
                                     }
                                  il=il.next;   
                                }
                              from=false;
                             }                                       
                         if (target==tos)
                            { il=ll.base;
                              while(il!=null)
                                { if(arg==il.name(language))
                                     { tftox.setText(""+il.x);
                                       tftoy.setText(""+il.y);
                                     }
                                  il=il.next;   
                                }
                              from=true;
                            }                                  
                       }   
    if (target instanceof TextField)
       { if(target==tffromx || target==tffromy)  
                if (froms.getSelectedIndex()!=0) {froms.select(0); from=false;}
         if(target==tftox || target==tftoy)  
                if (tos.getSelectedIndex()!=0){tos.select(0); from=true;}
       }    
    return false;
  } //action                            

double mx=0,my=0,ofmx=0,ofmy=0,otmx=0,otmy=0;
double fromx, fromy, tox, toy;
double factor=1.5;
double ox=181, oy=91;
boolean from=true;
boolean drawfromto=false;
boolean redraw=false;

String NS(double x) { if(x>0) return Math.abs(x) + "\u00b0 N";
                       else   return Math.abs(x) + "\u00b0 S";
                     }  
String EW(double x) { if(x>0) return Math.abs(x) + "\u00b0 E";
                       else   return Math.abs(x) + "\u00b0 W";
                     }  
                     
    
public boolean mouseMove(Event event, int x, int y)
  {mx=Math.round(x/factor-ox); my=Math.round(oy - y/factor);
   
   repaint();
   return true;
   }
   
public boolean mouseDown(Event even, int x, int y)
  { if(x<(362*factor) && y<(182*factor))
      {
    if(from) { fromx = x/factor-ox; fromy = oy - y/factor;
               tffromx.setText(""+Math.round(fromx));
               tffromy.setText(""+Math.round(fromy));
               if(froms.getSelectedIndex()!=0) {froms.select(0);}
               repaint();
             }
      else   { tox = x/factor-ox; toy = oy - y/factor; 
               tftox.setText(""+Math.round(tox));
               tftoy.setText(""+Math.round(toy));
               if(tos.getSelectedIndex()!=0) {tos.select(0);}
               drawfromto=true; 
               repaint();
             }
    from = !from;
      }
    return true;
  }   
public synchronized void update(Graphics g)
  { g.setColor(blauw);
    g.fillRect(0,(int)(180*factor+7),200,15);
    g.setColor(textkl);     
    if(mx<180 && my>-90) g.drawString(text(7) +  EW(mx) + ", " + NS(my) + "  ("+ 
       (from ? text(1) : text(2)) +")",0,(int)(180*factor+20));  
    if (redraw) {
    g.clearRect(0, 0, (int)(factor*360) + 2, (int)(factor*180) + 2);
    drawmap(g); redraw=false;}
    if (drawfromto) 
     { try{
          fromx=java.lang.Double.valueOf(tffromx.getText()).doubleValue();
          fromy=java.lang.Double.valueOf(tffromy.getText()).doubleValue();
          tox=java.lang.Double.valueOf(tftox.getText()).doubleValue();
          toy=java.lang.Double.valueOf(tftoy.getText()).doubleValue();
          } 
          catch(java.lang.NumberFormatException ee){} 
       double distance,distancekm;
       distance=Math.acos((new vector(fromy*Degree,fromx*Degree)).inner(
                 new vector(toy*Degree,tox*Degree))) * Rad;
       distancekm=distance / 360 * 40057;
       g.setColor(blauw);
       g.fillRect(0,(int)(180*factor+105),400,60);
       g.setColor(Color.red);
       g.drawString(text(5) + Math.round(
                              (alfa(new vector(fromy*Degree,fromx*Degree),
                                    new vector(toy*Degree,tox*Degree)) *Rad)
                                        )
                            + text(3) + text(4),
                    0,(int)(180*factor+120)); 
       g.drawString(text(6) + Math.round(distance) + text(3) + "= " 
                            + Math.round(distancekm) + " km",
                    0,(int)(180*factor+140)); 
                    
       double xx,yy,oxx,oyy,a;
       xx=fromx; yy=fromy;
       oxx=xx; oyy=yy;
       double acute=0.1;
       if(!(fromx==tox && fromy==toy))
       for(double i=0; i<=distance; i+=acute) // stop criterium moet wat slimmer nog.
          { a=alfa(new vector(yy*Degree,xx*Degree),
                   new vector(toy*Degree,tox*Degree)
                  );
            xx+=acute*Math.cos(a)/Math.cos(yy*Degree); // dit is niet goed.
            yy+=acute*Math.sin(a);
            if(xx>180) {xx-=360;oxx=xx;}
            if(xx<-180){xx+=360;oxx=xx;}
            g.drawLine((int)(factor*(oxx +ox)),(int)(factor*(oy-oyy))
                      ,(int)(factor*(xx  +ox)),(int)(factor*(oy-yy))
                      );
            oxx=xx; oyy=yy;
          }
       drawfromto=false; 
     }  
  }
  
public void drawmap(Graphics g)
  {g.setColor(grijs);
   g.fillRect(0, 0, (int)(factor*360) + 2, (int)(factor*180) + 2); 
   g.draw3DRect(0, 0, (int)(factor*360) + 2, (int)(factor*180) + 2, true);
   Color groen = new Color(0,100,0);
   try{
    InputStream is = null;
    is = new URL(getDocumentBase(), worldfile).openStream();
    StreamTokenizer st = new StreamTokenizer(new BufferedInputStream(is));
    st.eolIsSignificant(true);
    st.commentChar('#');
    st.commentChar('%');
    double theta, phi, otheta=0, ophi=0;
    boolean point=true; 
       for(int tokenType = st.nextToken();
           tokenType != StreamTokenizer.TT_EOF;
           tokenType = st.nextToken())
          { switch(tokenType) 
               { case StreamTokenizer.TT_NUMBER:
                      theta=st.nval;
                      tokenType=st.nextToken();
                      if (tokenType != StreamTokenizer.TT_NUMBER) 
                         { g.drawString("Error in file!",0,10);}
                      else { phi = st.nval; 
                             if(point) { point=false;
                                         otheta=theta; ophi=phi;
                                       }
                                else   { g.setColor(groen);
                                         g.drawLine((int)(factor*(otheta+ox)),(int)(factor*(oy-ophi)),
                                                    (int)(factor*(theta+ox)),(int)(factor*(oy-phi))
                                                   );
                                         otheta=theta; ophi=phi;
                                       }
                            }
                      break;
                 case StreamTokenizer.TT_EOL:
                      tokenType = st.nextToken();
                      if (tokenType == StreamTokenizer.TT_EOL)
                          { point=true;}
                      else{st.pushBack();}
                      break;
                      
                }
          }
        }
     catch (java.net.MalformedURLException e) {}
     catch (IOException e) {}
  }//drawmap
  
public void paint(Graphics g) 
  {  drawmap(g);
  } // paint


public static void main(String[] args)
{ vector Mekko= new vector(21.5 * Degree, 39.97 * Degree);
  vector Utrehxto = new vector(52.10 * Degree, 5.12 * Degree);
 
  System.out.println("hoi!");
  System.out.println("utrecht -> mekka: " + alfa(Utrehxto, Mekko) * Rad);
    StringTokenizer t = new StringTokenizer("Amsterdam 10 30 34 40"," "); 
    String hulp1, hulp2, hulp3, hulp4, hulp5;
    while(t.hasMoreTokens())   
        { hulp1=t.nextToken();
          hulp2=t.nextToken();
          hulp3=t.nextToken();
          if(t.hasMoreTokens()) hulp4=t.nextToken();
                else hulp4="*";
          if (hulp4.compareTo(",")!=0 && hulp4.compareTo("*")!=0) 
                          { hulp5=t.nextToken();
                            System.out.println(hulp1);
                            System.out.println(hulp2);
                            System.out.println(hulp3);
                            System.out.println(hulp4);
                            System.out.println(hulp5);
                            if(t.hasMoreTokens()) t.nextToken();
                           }
                    else  {System.out.println(hulp1);
                            System.out.println(hulp2);
                            System.out.println(hulp3);
                          }        
         }      


}

}// mekka
