package greenfoot.importer.scratch;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import bluej.extensions.SourceType;
import bluej.utility.Debug;

public abstract class ScriptableScratchMorph
extends Morph{    
   private String[] costumes;
   
   private String mungedName;
   
   private File javaFile;

   
   public ScriptableScratchMorph(int id, int version,
           
   List<ScratchObject> scratchObjects)
   {
      super(id, version, scratchObjects);     
      }
    
   
   
   
    
   
   @Override public int fields()
   {
      return super.fields() + 6;              
      }
    
   
   
   
   
   private String getObjName()
   {
      return (String)scratchObjects.get(super.fields() + 0).getValue();     
      }
    
   
   public String getObjNameJava()
   {
      if (mungedName == null) {
         Debug.message("Munging: " + getObjName());
         mungedName = ScratchImport.mungeUnique(getObjName());
         Debug.message("Munged to: " + mungedName);         
         }
       
      return mungedName;     
      }
    
   
   public ScratchObjectArray getBlocks()
   {
      return (ScratchObjectArray)scratchObjects.get(super.fields() + 2);     
      }
    
   
   public ScratchObjectArray getMedia()
   {
      return (ScratchObjectArray)scratchObjects.get(super.fields() + 4);     
      }

   
   public ImageMedia getCostume()
   {
      return (ImageMedia)scratchObjects.get(super.fields() + 5);     
      }
    
   
   public ImageMedia[] getCostumes()
   {        
      ArrayList<ImageMedia> imgs = new ArrayList<ImageMedia>();
      ScratchObjectArray objArray = (ScratchObjectArray)scratchObjects.get(super.fields() + 4);
       
      for (ScratchObject o : objArray.getValue()) {
         if (o instanceof ImageMedia) {
            imgs.add((ImageMedia)o);             
            }         
         }
       
      return imgs.toArray(new ImageMedia[0]);     
      }
    
   
   public int getCostumeCount()
   {
      return ((ScratchObjectArray)scratchObjects.get(super.fields() + 4)).getValue().length;     
      }
    
   
   protected abstract String greenfootSuperClass();
   
   protected abstract void constructorContents(StringBuilder acc);

   
   @Override
   
   public File saveInto(File destDir, Properties props, String prefix) throws IOException
   {
      if (javaFile != null) return javaFile;
        
       
      
      ScratchObject imageMedia = getCostume();            
      String className = getObjNameJava();
        
       
      StringBuilder acc = new StringBuilder();
       
      acc.append("import greenfoot.*;\npublic class " + className);
        
      String superClass = greenfootSuperClass();
      if (superClass != null) {
         acc.append(" extends ").append(superClass);         
         }
        
       
      costumes = new String[getCostumeCount()];
        
      acc.append("\n{\n");
       
      acc.append("private static final String[] COSTUMES = new String[] {");
       
      int i = 0;
       
      int curCostume = 0;
       
      for (ImageMedia img : getCostumes()) {
         if (i != 0) acc.append(", ");
         costumes[i] = img.saveInto(destDir, props, className + "_").getName();
         acc.append("\"").append(costumes[i]).append("\"");
         if (img == imageMedia) {                
            curCostume = i;             
            }
           
         i += 1;         
         }
       
      acc.append("};\n");
       
      acc.append("private static final int[] X_OFFSETS = new int[] {");
       
      i = 0;
       
      for (ImageMedia img : getCostumes()) {
         if (i != 0) acc.append(", ");
         acc.append(
         getScaleAmount().x.multiply(
                   
         new BigDecimal(img.getWidth() / 2).subtract(
         img.getRotationCentre().x
                   
         )
         ).intValue());
           
         i++;         
         }
       
      acc.append("};\n");
        
       
      acc.append("private static final int[] Y_OFFSETS = new int[] {");
       
      i = 0;
       
      for (ImageMedia img : getCostumes()) {
         if (i != 0) acc.append(", ");
         acc.append(
         getScaleAmount().y.multiply(
                     
         new BigDecimal(img.getHeight() / 2).subtract(
         img.getRotationCentre().y
                     
         )
         ).intValue());
           
         i++;         
         }
       
      acc.append("};\n");
        
       
      acc.append("private int curCostume = ").append(curCostume).append(";\n");
        
       
      acc.append("public " + className + "()\n{\n");
      constructorContents(acc);    
       
      acc.append("}\n");
        
      addHelpers(acc);

       
      codeForScripts(getBlocks(), acc, new LoopVarIterator());
       
      acc.append("}\n");
        
       
      javaFile = new File(destDir, className + "." + SourceType.Java.toString().toLowerCase());
       
      FileWriter javaFileWriter = new FileWriter(javaFile);
      javaFileWriter.write(acc.toString());
      javaFileWriter.close();
        
       
      for (ScratchObject media : getMedia()) {
         media.saveInto(destDir, props, className + "_");         
         }
        
        
      File imageFile = imageMedia.saveInto(destDir, props, className + "_");
      props.setProperty("class." + className + ".image", imageFile.getName());
        
       
      return javaFile;     
      }

   
   protected void addHelpers(StringBuilder acc)
   {     
   }

   
private void codeForBlock(ScratchObject block, StringBuilder decl, StringBuilder method, LoopVarIterator loopVars)
   {        
   
   
   
   
   ScratchObject[] blockContents = (ScratchObject[])block.getValue();
        
   if ("doRepeat".equals(blockContents[0].getValue())) {
      String var = loopVars.next();
           
      method.append("for (int " + var + " = 0; " + var + " < ").append(blockContents[1].getValue()).append(";" + var + "++)\n{\n");
           
      codeForBlock(blockContents[2], decl, method, new LoopVarIterator(loopVars));
           
      method.append("}\n");         
      }

   else if ("doPlaySoundAndWait".equals(blockContents[0].getValue())) {
      String soundName = ScratchImport.mungeUnique("snd" + (String)blockContents[1].getValue());
      decl.append("GreenfootSound ").append(soundName).append(";\n");
      method.append("if (" + soundName + " == null || !" + soundName + ".isPlaying()) {\n")
                 
      .append(soundName).append(" = new GreenfootSound(\"")
      .append(getObjNameJava()).append("_")
      .append(blockContents[1].getValue())
      .append(".wav\");\n")
                 
      .append(soundName).append(".play();\n}\n");
           
               
      }

   else if ("playSound:".equals(blockContents[0].getValue())) {            
      method.append("new GreenfootSound(\"")
      .append(getObjNameJava()).append("_")
      .append(blockContents[1].getValue())
      .append(".wav\").play();\n");         
      }

   else if ("setGraphicEffect:to:".equals(blockContents[0].getValue())) {
      if ("ghost".equals(blockContents[1].getValue())) {
         method.append("getImage().setTransparency(")
                     
         .append(new BigDecimal(255).subtract(((BigDecimal)blockContents[2].getValue()).multiply(BigDecimal.valueOf(255.0 / 100.0))).intValue())
         .append(");\n");             
         }         
      }

   else if ("hide".equals(blockContents[0].getValue())) {
      method.append("getImage().setTransparency(0);\n");         
      }

   else if ("show".equals(blockContents[0].getValue())) {
      method.append("getImage().setTransparency(255);\n");         
      }

   else if ("lookLike:".equals(blockContents[0].getValue())) {
      if (blockContents[1].getValue() instanceof String) {
         String costumeRoot = getObjNameJava() + "_" + (String)blockContents[1].getValue();
                
         int costumeFile = findCostume(costumeRoot + ".png");
         if (costumeFile >= 0) {                    
            costumeRoot += ".png";                
            }

         else {
         costumeFile = findCostume(costumeRoot + ".jpg");
         if (costumeFile >= 0) costumeRoot += ".jpg";                 
         }
      if (costumeFile >= 0) {
         method.append("curCostume = ")
         .append(costumeFile)
         .append(";\n");
         method.append("setImage(\"").append(costumeRoot).append("\");\n");
         method.append("getImage().scale(")
         .append(getBounds().x2.subtract(getBounds().x).intValue())
         .append(", ")
         .append(getBounds().y2.subtract(getBounds().y).intValue())
         .append(");\n");
         method.append("getWorld().repaint();\n");                
         }

      else {
      Debug.message("Could not locate costume: " + costumeFile);                 
      }             
   }         
}

else if ("forward:".equals(blockContents[0].getValue())) {
   method.append("move(").append(blockContents[1].getValue()).append(");\n");         
   }

else if ("bounceOffEdge".equals(blockContents[0].getValue())) {
   method.append("if (atWorldEdge()) turn(180);\n");         
   }

else if ("randomFrom:to:".equals(blockContents[0].getValue())) {
   int from = (Integer)blockContents[1].getValue();
   int to = (Integer)blockContents[2].getValue();
   if (from == 0) {
      method.append("Greenfoot.randomNumber(").append(to).append(")");            
      }

   else {
   method.append("(Greenfoot.randomNumber(").append(to - from).append(") + ").append(from).append(")");             
   }         
}

else if ("say:duration:elapsed:from:".equals(blockContents[0].getValue())) {
   if (blockContents.length >= 3) {                
      method.append("{\nBubble bubble = new Bubble(\"")
      .append((String)blockContents[1].getValue())
      .append("\");\n");
      method.append("getWorld().addObject(bubble, getX(), getY());");
      method.append("getWorld().repaint();\n");
               
      codeForBlock(new ScratchObjectArray(new ScratchObject[] {new ScratchPrimitive("wait:elapsed:from:"), blockContents[2]
      }), decl, method, loopVars);
   method.append("getWorld().removeObject(bubble);\n}\n");             
   }         
}

else if ("gotoX:y:".equals(blockContents[0].getValue())) {
   method.append("setLocation((getWorld().getWidth() / 2) + ")
   .append(((BigDecimal)blockContents[1].getValue()).intValue())
   .append(" + X_OFFSETS[curCostume]")
   .append(", (getWorld().getHeight() / 2) - ")
   .append(((BigDecimal)blockContents[2].getValue()).intValue())
   .append(" + Y_OFFSETS[curCostume]")
   .append(");\n");         
   }

else if ("turnRight:".equals(blockContents[0].getValue())) {
   String degrees = blockContents[1].getValue().toString();
   method.append("turn(-").append(degrees).append(");\n");         
   }

else if ("nextCostume".equals(blockContents[0].getValue())) {
   method.append("curCostume = (curCostume + 1) % COSTUMES.length;\n");
   method.append("setImage(COSTUMES[curCostume]);\n");
   method.append("getImage().scale(")
   .append(getBounds().x2.subtract(getBounds().x).intValue())
   .append(", ")
   .append(getBounds().y2.subtract(getBounds().y).intValue())
   .append(");\n");
   method.append("getWorld().repaint();\n");         
   }

else if ("wait:elapsed:from:".equals(blockContents[0].getValue())) {
   if (blockContents[1].getValue() instanceof BigDecimal) {
      BigDecimal seconds = (BigDecimal) blockContents[1].getValue();
      method.append("try {\n");
      method.append("Thread.sleep(").append(seconds.scaleByPowerOfTen(3).intValue()).append(");\n");
               
      method.append("} catch (InterruptedException e) { }");             
      }         
   }

else if (blockContents[0] instanceof ScratchObjectArray) {            
   for (ScratchObject blockContent : blockContents) {
      codeForBlock(blockContent, decl, method, loopVars);             
      }         
   }
       

else {
           
StringBuilder tmp = new StringBuilder();
           
for (ScratchObject o : blockContents) {
   tmp.append(o).append(",");             
   }
Debug.message("Unknown Scratch block/code: " + tmp.toString());         
}     
}

   
private int findCostume(String searchFor)
   {        
   for (int i = 0; i < costumes.length;i++) {
      if (searchFor.equals(costumes[i])) {                
         return i;             
         }         
      }
       
   return -1;     
   }

   
private void codeForScripts(ScratchObjectArray scripts, StringBuilder acc, LoopVarIterator loopVars)
   {        
   StringBuilder decl = new StringBuilder();
       
   StringBuilder method = new StringBuilder();
       
   StringBuilder firstTimeCode = new StringBuilder();
        
       
   
   
   for (ScratchObject scriptChunk : scripts.getValue()) {
      ScratchObject[] info = (ScratchObject[])scriptChunk.getValue();
           
      
      
      
            
      ScratchObject[] blocks = (ScratchObject[])info[1].getValue();
            
           
      
      
            
           
      
      
    
      ScratchObject[] firstBlockContents = (ScratchObject[])blocks[0].getValue();
            
      if ("MouseClickEventHatMorph".equals(firstBlockContents[0].getValue())) {
         if ("Scratch-MouseClickEvent".equals(firstBlockContents[1].getValue())) {
            method.append("if (Greenfoot.mouseClicked(this)) {");
                   
            
            for (ScratchObject block : Arrays.copyOfRange(blocks, 1, blocks.length)) {
               codeForBlock(block, decl, method, loopVars);                     
               }
                    
                   
            method.append("}");                 
            }             
         }

      else if ("EventHatMorph".equals(firstBlockContents[0].getValue())) {
         if ("Scratch-StartClicked".equals(firstBlockContents[1].getValue())) {                    
            
            
                    
            ScratchObject[] subsequentBlocks = Arrays.copyOfRange(blocks, 1, blocks.length);
                    
            if (subsequentBlocks.length == 1 && isLoop(subsequentBlocks[0])) {
               codeForBlock(subsequentBlocks[0], decl, method, loopVars);                    
               }

            else {
                        
                       
            int i;
                       
            for (i = 0; i < subsequentBlocks.length;i++) {
               ScratchObject[] blockContents = (ScratchObject[]) subsequentBlocks[i].getValue();
               if (!isLoop(blockContents[0])) {
                  codeForBlock(subsequentBlocks[i], decl, firstTimeCode, loopVars);                            
                  }

               else {
                               
               break;                             
               }                         
            }

         if (i < subsequentBlocks.length) {
            ScratchObject[] blockContents = (ScratchObject[]) subsequentBlocks[i].getValue();
                           
            
            if ("doForever".equals(blockContents[0].getValue())) {                                
               
               codeForBlock(blockContents[1], decl, method, loopVars);                            
               }

            else if ("doRepeat".equals(blockContents[0].getValue())) {                                
               decl.append("private int loopCount = 0;\n");
                                
               method.append("if (loopCount < ")
               .append(blockContents[1].getValue())
               .append(") {\n");
               method.append("loopCount += 1;\n");
               codeForBlock(blockContents[2], decl, method, loopVars);
                               
               method.append("}\n");                             
               }                         
            }                     
         }                 
      }            
   }

else if ("KeyEventHatMorph".equals(firstBlockContents[0].getValue())) {
   String scratchKeyName = (String) firstBlockContents[1].getValue();
               
   String greenfootKeyName = null;
   if (scratchKeyName.endsWith(" arrow")) {
      greenfootKeyName = scratchKeyName.substring(0, scratchKeyName.length() - " arrow".length());                 
      }
                
   if (greenfootKeyName != null) {
      method.append("if (Greenfoot.isKeyDown(\"" + greenfootKeyName + "\")) {\n");
                    
      ScratchObject[] subsequentBlocks = Arrays.copyOfRange(blocks, 1, blocks.length);
                   
      for (ScratchObject block : subsequentBlocks) {
         codeForBlock(block, decl, method, loopVars);                     
         }
                   
      method.append("}\n");                 
      }             
   }
           

else {
               

Debug.message("Ignoring block headed: " + firstBlockContents[0].getValue());             
}                     
}
        
acc.append(decl);
if (firstTimeCode.length() > 0) {            
   acc.append("private boolean firstTime = true;\n");         
   }
       
acc.append("public void act()\n{\n");
if (firstTimeCode.length() > 0) {
   acc.append("if (firstTime) {\n");
   acc.append("firstTime = false;\n");
   acc.append(firstTimeCode);
           
   acc.append("}\n");         
   }
acc.append(method);
       
acc.append("}\n");     
}

   
private static boolean isLoop(ScratchObject scratchObject)
   {
   return "doForever".equals(scratchObject.getValue()) || "doRepeat".equals(scratchObject.getValue());     
   }

   
public ScratchPoint getScaleAmount()
   {        
   return new ScratchPoint(new BigDecimal(1), new BigDecimal(1));     
   }
    
   
private static class LoopVarIterator
{           
int i = 0;
        
       
   public LoopVarIterator()
{   
}
       
public LoopVarIterator(LoopVarIterator it)
{
i = it.i; 
   }

       
public String next()
       {
   switch (i++)
           {            
      case 0: return "i";
           
      case 1: return "j";
           
      case 2: return "k";
      default: return "i" + (i - 2);             
      }         
   }      
} 
}
top, use, map, abstract class ScriptableScratchMorph

.   ScriptableScratchMorph
.   fields
.   getObjName
.   getObjNameJava
.   getBlocks
.   getMedia
.   getCostume
.   getCostumes
.   getCostumeCount
.   greenfootSuperClass
.   constructorContents
.   saveInto
.   addHelpers
.   codeForBlock
.   findCostume
.   codeForScripts
.   isLoop
.   getScaleAmount

top, use, map, class LoopVarIterator

.   LoopVarIterator
.   LoopVarIterator
.   next




647 neLoCode + 0 LoComm