//--------------------------------------------
//   ""   Nokia
//
//    
// "    "
//
// (c) Voolkan
//--------------------------------------------

import javax.microedition.midlet.MIDlet;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.ChoiceGroup;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.ImageItem;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.StringItem;

import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordStoreException;

import java.util.Vector;
import java.util.Random;

import java.io.IOException;

import com.nokia.mid.ui.FullCanvas;
import com.nokia.mid.ui.DeviceControl;
import com.nokia.mid.sound.Sound;


//   SnakeGame,    
public class SnakeGame extends MIDlet implements CommandListener {

   private Display display;         //  
   private Snake curSnake;          //  
   private List menu;               //  
   private Command ok;              //    
   private ChoiceGroup levelChoice; //     
   private Command set;             //     


   public void destroyApp(boolean destroy) {
      curSnake=null;
      notifyDestroyed();
   }


   public void pauseApp() {}


   //   
   public void startApp() {
      //       
      String menuOptions[] = {"New Game","Set Level","High Score"};
      //   
      menu = new List("",List.IMPLICIT,menuOptions,null);   
      //     
      ok = new Command("Ok", Command.OK, 1);
      //    
      menu.addCommand(ok);
      //     
      set = new Command("Set", Command.BACK, 1);
      //      
      menu.setCommandListener(this);
      //     
      display = Display.getDisplay(this);

      //      
      Image title = null; 
      try {
         //   
         title = Image.createImage("/title.png");
      } catch (IOException ioe) {}

      //    
      ImageItem item = new ImageItem("",title,ImageItem.LAYOUT_CENTER,"");
      //  
      showNewScreen("SNAKE",ok,item);
   }


   //   
   public void commandAction(Command c, Displayable d) {
      //    Ok    
      if (c == ok && d == menu) {
         //    
         int selIndex = menu.getSelectedIndex();
         switch(selIndex) {
            //   :  
            case 0 :
               //   
               curSnake = new Snake();
               //     
               Thread moveThread = new Thread(curSnake);
               //   
               moveThread.start();
               //    
               display.setCurrent(curSnake);
               break;
            //   :   
            case 1 :
               //       
               String levelOptions[] = {"Level 1","Level 2",
                                        "Level 3","Level 4"};
               //        
               levelChoice = new ChoiceGroup("",List.EXCLUSIVE,
                                             levelOptions,null);
               //   ,  
               //   
               levelChoice.setSelectedIndex(5-getParameter(1),true);
               //    
               showNewScreen("Set Level",set,levelChoice);
               break;
            //   :   
            case 2 :
               //     
               String strScore = (new Integer(getParameter(0))).toString();
               //   -
               StringItem highScoreItem = new StringItem("",strScore);
               //   
               showNewScreen("HIGH SCORE",ok,highScoreItem);
               break;
         }
      }

      //    Ok,    
      if(c==ok && d!=menu)
         //     
         display.setCurrent(menu);

      //    Set     
      if(c == set) {
         try {
            //   
            byte buff[] = {0,5};
            //      "SNAKE"
            RecordStore recordStore = RecordStore.openRecordStore("SNAKE",
                                                                   true);
            //    
            RecordEnumeration re = recordStore.enumerateRecords(null, null,
                                                                false);
            //    
            if (re.numRecords()!=0) {
               //  id   
               int id = re.nextRecordId();
               //     
               buff = recordStore.getRecord(id);
               //    
               buff[1] = (byte)(5 - levelChoice.getSelectedIndex());
               //   
               recordStore.setRecord(id,buff,0,2);
            } else {
               //     
               recordStore.addRecord(buff, 0, 2);
            }

         } catch(RecordStoreException rse) {
         }
         //     
         display.setCurrent(menu);
      }
   }


   //  showNewScreen       item
   private void showNewScreen(String title,Command c,Item item) {
      //     
      Form newScreenForm = new Form(title);
      //      
      newScreenForm.addCommand(c);
      //    
      newScreenForm.append(item);
      //    
      newScreenForm.setCommandListener(this);
      //    
      display.setCurrent(newScreenForm);
   }
   

   //  getParameter      
   //     ,    index
   private byte getParameter(int index) {
      //   
      byte buff[] = {0,0};
      try {
         //      "SNAKE"
         RecordStore recordStore = RecordStore.openRecordStore("SNAKE",
                                                                true);
         //    
         RecordEnumeration re = recordStore.enumerateRecords(null, null,
                                                             false);
         //    
         if (re.numRecords()!=0) {
            //  id     
            buff = recordStore.getRecord(re.nextRecordId());
         }
      } catch(RecordStoreException rse) {
      }
      //  
      return buff[index];
   }


   //  SnakePart    
   private class SnakePart extends Object {

      private int x;      //  
      private int y;      //  y
      private int part;   //    
      private int dir;   //   

      // ;     
      public SnakePart(int _x, int _y, int _part, int _dir) {
         x=_x;
         y=_y;
         part=_part;
         dir=_dir;
      }
      //     
      private int getPart() { return part; }
      //    
      private int getDir() { return dir; }
      //   
      private int getX() { return x; }
      //   y
      private int getY() { return y; }
      //     
      private void setPartDir(int _part, int _dir) {
         //    -1, 
         //    
         part=_part;
         if(_dir!=-1) dir=_dir;
      }
   }


   //  
   private class Snake extends FullCanvas implements Runnable {

   // ,   
   private int UP = 0;        // 
   private int DOWN = 3;      // 
   private int LEFT = 1;      // 
   private int RIGHT = 2;     // 

   private int HEAD = 0;      //  
   private int TAIL = 1;      //  
   private int BODY = 2;      //  
   private int ACLOCKWISE_TURN = 3;   //     
   private int CLOCKWISE_TURN = 4;    //     

   private Vector snake;      //   
   private int width;         //   
   private int height;        //  
   private int xHead;         //    
   private int yHead;         //    
   private Image heart;       //  
   private int xHeart;        //   
   private int yHeart;        //  y 
   private Image[] images;    //    
   private int imageSize;     //    

   private int direction;     //   
   private boolean eatFlag;   //   
   private boolean gameOverFlag;    //   
   
   private RecordStore recordStore; //  
   private int recordID;      // ID  
   private byte level;        //   
   private byte speed;        //    
   private byte highScore;    //  


   //   Snake
   public Snake() {
      //   
      super();
      //    - 
      images = new Image[20];
      //      
      try {
         //    
         images[0] = Image.createImage("/HeadUp.png");
         images[1] = Image.createImage("/HeadLeft.png");
         images[2] = Image.createImage("/HeadRight.png");
         images[3] = Image.createImage("/HeadDown.png");
         //    
         images[4] = Image.createImage("/TailUp.png");
         images[5] = Image.createImage("/TailLeft.png");
         images[6] = Image.createImage("/TailRight.png");
         images[7] = Image.createImage("/TailDown.png");
         // 
         images[8] = Image.createImage("/BodyUp.png");
         images[9] = Image.createImage("/BodyLeft.png");
         images[10] = images[9];
         images[11] = images[8];
         //     
         images[12] = Image.createImage("/TurnDownLeft.png");
         images[13] = Image.createImage("/TurnUpLeft.png");
         images[14] = Image.createImage("/TurnDownRight.png");
         images[15] = Image.createImage("/TurnUpRight.png");
         //     
         images[16] = images[14];
         images[17] = images[12];
         images[18] = images[15];
         images[19] = images[13];
         // 
         heart = Image.createImage("/heart.png");
      }
      catch (IOException ioe) {}
      
      imageSize = images[0].getWidth();
      //  ,   
      snake = new Vector();
      //   
      width = getWidth();
      //   
      height = getHeight();
      //      
      //     imageSize
      xHead = ((width/2)/imageSize)*imageSize;
      yHead = ((height/2)/imageSize)*imageSize;
      //    
      SnakePart partS = new SnakePart(xHead,yHead,HEAD,UP);
      //    
      snake.addElement(partS);
      //    
      partS = new SnakePart(xHead,yHead+imageSize,BODY,UP);
      snake.addElement(partS);
      //    
      partS = new SnakePart(xHead,yHead+2*imageSize,TAIL,UP);
      snake.addElement(partS);
      //    
      direction = UP;
      //     
      setNewHeart();
      //    
      gameOverFlag = false;

      //    
      //   -  
      //   -   
      byte buff[] = {0,5};
      try {
         //      "SNAKE"
         recordStore = RecordStore.openRecordStore("SNAKE", true);
         //    
         RecordEnumeration re = recordStore.enumerateRecords(null, null,
                                                             false);
         //   
         if (re.numRecords()==0)
            //     
            recordID = recordStore.addRecord(buff, 0, 2);
         else
            //  id   
            recordID = re.nextRecordId();
         //    
         buff = recordStore.getRecord(recordID);
         //  :  
         highScore = buff[0];
         //  :   
         level = buff[1];
         //    
         speed = 100;
      } catch(RecordStoreException rse) {
      } 
   }


   //   
   public void paint(Graphics g) {

      //  
      g.setColor(0xFFFFFF);
      g.fillRect(0,0,width,height);
      g.setColor(0x000000);
      //  
      g.drawRect(0,0,width-1,height-1);
      //   
      int snakeLen = snake.size();
      //     
      if ( gameOverFlag ) {
         //    100 
         DeviceControl.startVibra(100,100);
         //    
         g.drawString("GAME OVER",width/2,height/2-10,g.HCENTER|g.TOP);
         //    
         String scoreStr;
         //   
         byte buff[] = new byte[2];
         //    
         if(highScore < snakeLen) {
            highScore = (byte)snakeLen;
            //    
            buff[0] = highScore;
            buff[1] = level;   
            try {
               //     
               recordStore.setRecord(recordID,buff,0,2);
            } catch(RecordStoreException rse) {
            }
            //    
            g.setColor(0xff0000);
            //     
            scoreStr = new String("HIGH SCORE: "+snakeLen);
         //    
         } else 
            //    
            scoreStr = new String("YOUR SCORE: "+snakeLen);
         //  
         g.drawString(scoreStr,width/2,height/2+10,g.HCENTER|g.TOP);
         //         
      } else {
         //    
         for ( int i=0; i<snakeLen; i++ ){
            //  ,   
            //         
            g.drawImage(images[4*((SnakePart)snake.elementAt(i)).getPart()+
               ((SnakePart)snake.elementAt(i)).getDir()],
               ((SnakePart)snake.elementAt(i)).getX(),
               ((SnakePart)snake.elementAt(i)).getY(),
               g.HCENTER | g.VCENTER);
         }
         //   
         g.drawImage(heart,xHeart,yHeart,g.HCENTER | g.VCENTER);
      }
   }


   //  setNewHeart;     
   public void setNewHeart() {
      //       
      int xPartMaxNum = (width-5)/imageSize;
      //       
      int yPartMaxNum = (height-5)/imageSize;
      //   
      int len = snake.size();   
      //     
      Random rnd = new Random();
      //   
      boolean setFlag = false;
      // ,      
      while ( !setFlag ) {   
         //    
         setFlag = true;
         //    
         xHeart = imageSize*(Math.abs(rnd.nextInt())%xPartMaxNum+1);
         //    
         yHeart = imageSize*(Math.abs(rnd.nextInt())%yPartMaxNum+1);
         //     
         //    
         for ( int i=0; i<len-1; i++ )         
            if ( xHeart == ((SnakePart)(snake.elementAt(i))).getX() && 
               yHeart == ((SnakePart)(snake.elementAt(i))).getY()) 
         //    
         setFlag = false;
      }
   }


   //  keyPressed   
   public synchronized void keyPressed(int keyCode) {
      //      
      if ( !gameOverFlag ) {
         switch (keyCode) {
            // ,      
            //     
            case KEY_NUM2:
               checkMove(xHead, yHead-imageSize);
               moveUp();
               break;
            case KEY_NUM4:
               checkMove(xHead-imageSize, yHead);
               moveLeft();
               break;
            case KEY_NUM6:
               checkMove(xHead+imageSize, yHead);
               moveRight();
               break;
            case KEY_NUM8:
               checkMove(xHead, yHead+imageSize);
               moveDown();
               break;
         }

         //   
         repaint();
      } else {
         // GameOver!   
         display.setCurrent(menu);
      }
   }


   //    
   public void run() {
      //      
      while (!gameOverFlag) {
         //   
         synchronized (this) {
            //     
            if(direction==UP) {checkMove(xHead, yHead-7); moveUp();}
            if(direction==LEFT) {checkMove(xHead-7, yHead); moveLeft();}
            if(direction==RIGHT) {checkMove(xHead+7, yHead); moveRight();}
            if(direction==DOWN) {checkMove(xHead, yHead+7); moveDown();}
         }
         //   
         repaint();
         try {
            //    sleep*level 
            Thread.sleep(level*speed);
         } catch (InterruptedException e) {
         }
      }
   }


   //   
   //     (xH,yH)
   public void checkMove(int xH, int yH) {
      //    
      for ( int i=3; i<snake.size()-1; i++ ) {
         //       
         if ( xH == ((SnakePart)(snake.elementAt(i))).getX() &&
            yH == ((SnakePart)(snake.elementAt(i))).getY())
            //    
            gameOverFlag=true;

      }
      //     
      //   
      if ( xH == xHeart && yH == yHeart ) {
         //    
         eatFlag = true;
         //      100  1000    50
         for(int i=100; i<=1000; i+=50) {
            //    
            Sound beep = new Sound(i,50);
            //   
            beep.play(1);
         }
         //  
         speed--;
      }
      else
         //    
         eatFlag = false;
   }


   //  
   private void moveLeft() {
      //    ,  
      if ( direction==RIGHT ) return;
      //    
      xHead-=imageSize;
      //      
      if ( xHead <= imageSize/2+1) { gameOverFlag=true; return; }
      //    
      SnakePart head = (SnakePart)(snake.firstElement());
      //    
      if ( direction==UP )
         //       
         head.setPartDir(ACLOCKWISE_TURN,LEFT);
      else {
         //    
         if ( direction==DOWN )
            //       
            head.setPartDir(CLOCKWISE_TURN,LEFT);
         else //    
            //    
            head.setPartDir(BODY,LEFT);
      }
      //     
      SnakePart sPart = new SnakePart(xHead,yHead,HEAD,LEFT);
      //     
      snake.insertElementAt(sPart,0);

      //     
      if (eatFlag) {
         //     
         setNewHeart();
      }
      else {
         //  ,
         //   
         snake.removeElement(snake.lastElement());
         //    
         ((SnakePart)snake.lastElement()).setPartDir(TAIL,-1);
      }
      //   
      direction = LEFT;
   }


   //  
   private void moveRight() {
      //    ,  
      if ( direction==LEFT ) return;
      //    
      xHead+=imageSize;
      //      
      if ( xHead >= width-imageSize/2) { gameOverFlag=true; return; }

      //     ,
      //       
      if ( direction == UP) ((SnakePart)(snake.firstElement())).
                  setPartDir(CLOCKWISE_TURN,RIGHT);
      else {
         //     ,
         //       
         if ( direction == DOWN) ((SnakePart)(snake.firstElement())).
                     setPartDir(ACLOCKWISE_TURN,RIGHT);
         //   ,    
         else ((SnakePart)(snake.firstElement())).setPartDir(BODY,RIGHT);
      }

      //     
      SnakePart sPart = new SnakePart(xHead,yHead,HEAD,RIGHT);
      snake.insertElementAt(sPart,0);
      //    
      if (eatFlag)
         //        
         setNewHeart();
      else {
         //  
         snake.removeElement(snake.lastElement());
         ((SnakePart)snake.lastElement()).setPartDir(TAIL,-1);
      }
      //    
      direction = RIGHT;
   }


   //  
   private void moveUp() {
      //    ,  
      if ( direction == DOWN ) return;

      //    
      yHead-=imageSize;
      //      
      if ( yHead <= imageSize/2+1) { gameOverFlag=true; return; }

      //     ,
      //       
      if ( direction==LEFT) ((SnakePart)(snake.firstElement())).
                  setPartDir(CLOCKWISE_TURN,UP);
      else {
         //     ,
         //       
         if ( direction==RIGHT) ((SnakePart)(snake.firstElement())).
                     setPartDir(ACLOCKWISE_TURN,UP);
         //   ,    
         else ((SnakePart)(snake.firstElement())).setPartDir(BODY,UP);
      }

      //     
      SnakePart sPart = new SnakePart(xHead,yHead,HEAD,UP);
      snake.insertElementAt(sPart,0);

      //    
      if (eatFlag) 
         //        
         setNewHeart();
      else {
         //  
         snake.removeElement(snake.lastElement());
         ((SnakePart)snake.lastElement()).setPartDir(TAIL,-1);
      }
      //    
      direction = UP;
   }


   //  
   private void moveDown() {
      //    ,  
      if ( direction==UP ) return;
      //    
      yHead+=imageSize;
      //      
      if ( yHead >= height-imageSize/2) { gameOverFlag=true; return; }

      //     ,
      //       
      if ( direction==LEFT) ((SnakePart)(snake.firstElement())).
                  setPartDir(ACLOCKWISE_TURN,DOWN);
      else {
         //     ,
         //       
         if ( direction==RIGHT) ((SnakePart)(snake.firstElement())).
                     setPartDir(CLOCKWISE_TURN,DOWN);
         //   ,    
         else ((SnakePart)(snake.firstElement())).setPartDir(BODY,DOWN);
      }   

      //     
      SnakePart sPart = new SnakePart(xHead,yHead,HEAD,DOWN);
      snake.insertElementAt(sPart,0);
      
      //    
      if (eatFlag) 
         //     
         setNewHeart();
      else {
         //  
         snake.removeElement(snake.lastElement());
         ((SnakePart)snake.lastElement()).setPartDir(TAIL,-1);
      }
      //    
      direction = DOWN;
   }
} // class Snake
} // class SnakeGame
