InitialObjectDescriptor {
  objectDescriptorID 1
  ODProfileLevelIndication 0xFF
  sceneProfileLevelIndication 0xFE
  audioProfileLevelIndication 0xFF
  visualProfileLevelIndication 0xFF
  graphicsProfileLevelIndication 0xFE

  esdescr [
    ES_Descriptor {
      es_id 1
      decConfigDescr DecoderConfigDescriptor {
				streamType 3
				decSpecificInfo BIFSConfig {
				  isCommandStream true
				  pixelMetric true
				  useNames true
				}
      }
    }
  ]
}

PROTO Bubble [
  exposedField SFInt32 index 0
  exposedField SFInt32 state 0
  exposedField SFInt32 display 0
  exposedField SFVec2f position 0 0

  exposedField SFFloat size 9
  exposedField SFColor color 0 0 0
  exposedField SFFloat red 0
  exposedField SFFloat green 0
  exposedField SFFloat blue 0

  exposedField SFFloat strokeWidth 2
  exposedField SFFloat strokeTransparency 0.3
  exposedField SFBool alignStroke FALSE

  eventOut SFInt32 isOver 
  eventOut SFInt32 isActive 
] {
  Switch { 
    whichChoice IS display 
    choice [  
      Transform2D { 
        translation IS position
        children [ 
          ColorTransform {
            mrr 0.7
            mgg 0.5
            mbb 0.9
            maa 0.6
            ta 0.274
            tr IS red
            tg IS green
            tb IS blue
            children [
              Shape {  
                geometry DEF C Circle { radius IS size } 
                appearance Appearance { 
                  material Material2D {
                    filled TRUE  
                  }
                  texture RadialGradient {
                    center 0.5 0.5
                    focalPoint 0.5 0.5
                    key [ 0 1 ]
                    keyValue [ 1 1 1 0 0 0 ]
                  } 
                } 
              } 
            ]
          }
          Shape {  
            geometry USE C 
            appearance Appearance { 
              material Material2D {
                filled FALSE
                lineProps XLineProperties { 
                  width 2
                  lineColor 0.2 0.2 0.2
                  transparency IS strokeTransparency
                  isCenterAligned IS alignStroke
                } 
              }
            } 
          } 
          DEF TS TouchSensor {}
          DEF Vindex Valuator {
            inSFInt32 IS index
          }
          DEF VOver Valuator {
            outSFInt32 IS isOver
          }
          DEF VActive Valuator {
            outSFInt32 IS isActive
          }
        ] 
      } 
    ] 
  }
  ROUTE Vindex.outSFFloat TO VOver.Factor1
  ROUTE Vindex.outSFFloat TO VActive.Factor1
  ROUTE TS.isOver TO VOver.inSFBool
  ROUTE TS.isActive TO VActive.inSFBool
}
OrderedGroup {
  children [
    WorldInfo {
      title "Classic Bubble Game"
      info [
        "This test shows the classic bubble game."
        "It features:" 
        " - a PROTO which defines a single Bubble node. This node takes as input the index of the bubble in the bubbleGrid. The bubble outputs this index on separate events: one when the mouse enters the bubble and one when a click on the bubble happens. This PROTO does not use any scripting for that." 
        " - a Script node which handles the initialization (colors, size, index) and dynamic positionning of the bubbles. It is also responsible for the game logic."
        "The number of of lines and columns in the bubble grid can be easily modified in the Script node." 
        "" 
        "GPAC Regression Tests" "$Date: 2008-11-24 14:58:25 $ - $Revision: 1.6 $" 
        "(C) 2002-2006 GPAC Team"        
      ]
    }
    DEF VIEWPORT Viewport { 
      size 410 300
      fit 1 
    }
    Background2D { backColor 1 1 1 }
    DEF CANVAS_TRANSFORM Transform2D {
      children [
        Shape {
          geometry DEF CANVAS Rectangle { size 0 0 }
          appearance Appearance {
            material Material2D {
              filled TRUE
              emissiveColor 1 1 1
              lineProps XLineProperties {
                width 2
                isScalable FALSE
                lineColor 0 0 0
              }
            }
          }
        }
        DEF BUBBLE_ROOT Transform2D {}
      ]
    }
    DEF TEXT_TRANSFORM Transform2D {
      children [
        Shape {
          geometry DEF SCORE_TEXT Text { 
            fontStyle FontStyle {
              size 20
              family "SANS"
              justify [ "BEGIN" "FIRST" ]
            } 
          }
          appearance Appearance {
            material Material2D {
              filled TRUE
              emissiveColor 0 0 0
            }
          }
        }
      ]
    }    
    DEF Vover Valuator {}
    DEF Vclick Valuator {}
    DEF BUBBLE_SCRIPT Script {
      field SFNode score_text   USE SCORE_TEXT
      field SFNode bubble_root    USE BUBBLE_ROOT
      
      field SFNode canvas       USE CANVAS
      field SFNode canvas_pos   USE CANVAS_TRANSFORM 
      field SFNode viewport     USE VIEWPORT
      field SFNode trans_text   USE TEXT_TRANSFORM
      
      field SFNode val_over     USE Vover
      field SFNode val_click    USE Vclick
      
      eventIn SFInt32 bubbleOnClick
      eventIn SFInt32 bubbleMouseOver
      
      url [ "javascript: 
      function initialize() {
        	bubbleGrid = new Array();
        	nbLines = 15;
        	nbCols = 26;
        	bubble_radius = 9;
        	score = 0;
        	neutral_state  = 0;
          selected_state = 1;
          exploded_state = 2;
          empty_state    = 4;
      		      		
          for (i=0; i<nbLines; i++) {
      			for (j=0; j<nbCols; j++) {
      				bubbleGrid[i*nbCols+j] = new bubble(bubble_root, i, j);
//      				print('index: '+(i*nbCols+j));
      			}
      		}
      		
      		gridWidth = nbCols*(bubble_radius+1)*2;
      		gridHeight = nbLines*(bubble_radius+1)*2;
      		
      		viewport.size = new SFVec2f(gridWidth, gridHeight+50);
      		canvas.size = new SFVec2f(gridWidth, gridHeight);
      		canvas_pos.translation = new SFVec2f(0, 50/2-2);
      		
        	score_text.string[0] = 'Score: 0';
        	bubble_root.scale = new SFVec2f(1, -1);
        	bubble_root.translation = new SFVec2f(-gridWidth/2 + 10, gridHeight/2-10);
          trans_text.translation = new SFVec2f(-gridWidth/2 + 10, -(gridHeight+50)/2+10);        	
        }

        function set_neutral(bubble) {
    	     bubble.state = neutral_state;
      		 bubble.alignStroke = FALSE;
           bubble.strokeWidth = 2;
           bubble.strokeTransparency = 0.7;
        }
        
        function set_selected(bubble) {
    	     bubble.state = selected_state;
      		 bubble.alignStroke = TRUE;
           bubble.strokeWidth = 3;
           bubble.strokeTransparency = 0.3;
        }

        function set_empty(bubble) {
    	     bubble.state = empty_state;
      		 bubble.display = -1;
        }
        
        function set_exploded(bubble) {
      		 bubble.display = -1;
      		 bubble.state = exploded_state;
      		 bubble.alignStroke = FALSE;
           bubble.strokeWidth = 2;
           bubble.strokeTransparency = 0.3;
        }

        function set_visible(bubble) {
          bubble.display = 0;
        }

        function bubble(parent, line, column) {          
          var bubble;
          bubble = new SFNode('_Proto1');
          bubble.position = new SFVec2f(2*column*(bubble_radius+1), 2*line*(bubble_radius+1));
          bubble.index = line*nbCols+column + 1; //1-based index
          bubble.size = bubble_radius;
          set_neutral(bubble);
          bubble.red = 170/255.0;
          bubble.green = Math.round(Math.random()*1)*150/255.0;
          bubble.blue = Math.round(Math.random()*1)*255/255.0;
          bubble.color = new SFColor(bubble.red, bubble.green, bubble.blue);
//          print('Color: red '+ bubble.red + ' green ' + bubble.green + ' blue '+bubble.blue);           
          parent.children[parent.children.length] = bubble;
          Browser.addRoute(bubble, 'isOver', val_over, 'inSFInt32');
          Browser.addRoute(bubble, 'isActive', val_click, 'inSFInt32');
          return bubble;
        }

        function bubbleMouseOver(value) {
          if (!value) return;
          value--;
        	if (bubbleGrid[value].state == neutral_state) {
            for (i=0; i<nbLines; i++) {
        	   for (j=0; j<nbCols; j++) {
        	    if (bubbleGrid[i*nbCols+j].state != empty_state) {
        	     set_neutral(bubbleGrid[i*nbCols+j]);
              }
             }
        	  }
        	  line = Math.floor(value / nbCols);
        	  column = value % nbCols;
//        	  print('MouseOver l: '+line+' c: '+column);
        		if (propagate(line, column, bubbleGrid[line*nbCols+column].color)==1) {
        	     set_neutral(bubbleGrid[line*nbCols+column]);
        		}
        	} 
        }
        
        function bubbleOnClick(value) {
          if (!value) return;
          value--;
      		if (bubbleGrid[value].state == selected_state) {
      		  line = Math.floor(value / nbCols);
      		  column = value % nbCols;
      			explode(line, column, bubbleGrid[value].color);
      			gravity();
      			connexity();
      		}
      	}

        function isColorEqual(c1, c2) {
          if (c1.r != c2.r) return 0;
          if (c1.g != c2.g) return 0;
          if (c1.b != c2.b) return 0;
          return 1;
        }
        
        function propagate(line, column, color) {          
        	var colmin, colmax, curcol, k, res;
//      	  print('propagate l: '+line+' c: '+column);
        	if ( isColorEqual(bubbleGrid[line*nbCols+column].color, color) && 
               (bubbleGrid[line*nbCols+column].state == neutral_state)) {
         		res = 1;
         		set_selected(bubbleGrid[line*nbCols+column]);
        		
        		//before the bubble on the same line
        		curcol = column-1;
        		while ( (curcol>=0) &&
                    isColorEqual(bubbleGrid[line*nbCols+curcol].color,color) && 
                    (bubbleGrid[line*nbCols+curcol].state == neutral_state)) {
//          	  print('propagate before l: '+line+' c: '+curcol);
           		set_selected(bubbleGrid[line*nbCols+curcol]);
        			curcol--;
        			res++;
        		}

        		//after the bubble on the same line
        		colmin = ++curcol;
        		curcol = column+1;
        		while ( (curcol<nbCols) &&
                    isColorEqual(bubbleGrid[line*nbCols+curcol].color, color) && 
                    (bubbleGrid[line*nbCols+curcol].state == neutral_state)) {
//          	  print('propagate after l: '+line+' c: '+curcol);
           		set_selected(bubbleGrid[line*nbCols+curcol]);
        			curcol++;
        			res++;
        		}

        		colmax = --curcol;
        		for (k=colmin; k<=colmax; k++) {
        			if (line>0) {
        				if (isColorEqual(bubbleGrid[(line-1)*nbCols+k].color, color)&& 
        					(bubbleGrid[(line-1)*nbCols+k].state == neutral_state))
        						res = res + propagate(line-1, k, color);
        			}
        			if (line<nbLines-1) {
        				if (isColorEqual(bubbleGrid[(line+1)*nbCols+k].color, color)&& 
        					(bubbleGrid[(line+1)*nbCols+k].state == neutral_state))
        						res = res + propagate(line+1, k, color);
        			}
        		}        		
            return res;
        	} else {
        	  return 0;
          }
        }
        
        function explode(line, column, color) {
//      	  print('explode l: '+line+' c: '+column);
        	if (isColorEqual(bubbleGrid[line*nbCols+column].color, color) && 
              (bubbleGrid[line*nbCols+column].state == selected_state)) {
        	  set_exploded(bubbleGrid[line*nbCols+column]);
        		if (line>0) 			explode(line-1, column, color);
        		if (column>0)			explode(line, column-1, color);					
        		if (column<nbCols-1)	explode(line, column+1, color);
        		if (line<nbLines-1)		explode(line+1, column, color);
        	}
        }

        function gravity() {
          exploded = 0;
          for (j=0; j<nbCols; j++) {
           	for (i=0; i<nbLines; i++) {
           		if (bubbleGrid[i*nbCols+j].state == exploded_state) {
           			start = end = i;
           			while ((end<nbLines)&&(bubbleGrid[end*nbCols+j].state == exploded_state)) {
        				  // ????
        				  set_visible(bubbleGrid[end*nbCols+j]);
           				end++;
           			}
           			lg = end-start;
           			exploded +=lg;
           			for (k=end-1; k>=0; k--) {
           				if (k-lg>=0) {
           					if (bubbleGrid[(k-lg)*nbCols+j].state != empty_state) {
            					movebubble(k-lg, j, k, j);
            				} else {
            				  set_empty(bubbleGrid[k*nbCols+j]);         							
            				}
             			} else {
             			  set_empty(bubbleGrid[k*nbCols+j]);
             			}
           			}
           		}
           	}
          }
          score += exploded * exploded;
        	score_text.string[0] = 'Score: '+score;
        }
        
        function movebubble(lsrc, csrc, ldst, cdst) {
//        	  print('Move src l: '+lsrc+' c: '+csrc);
//        	  print('Move dst l: '+ldst+' c: '+cdst);
        	// move a bubble from the (lsrc, csrc) position to the (ldst, cdst) position
        	// and let the src empty
        	bubbleGrid[ldst*nbCols+cdst].color = bubbleGrid[lsrc*nbCols+csrc].color;
        	bubbleGrid[ldst*nbCols+cdst].red = bubbleGrid[lsrc*nbCols+csrc].red;
        	bubbleGrid[ldst*nbCols+cdst].green = bubbleGrid[lsrc*nbCols+csrc].green;
        	bubbleGrid[ldst*nbCols+cdst].blue = bubbleGrid[lsrc*nbCols+csrc].blue;
        	bubbleGrid[ldst*nbCols+cdst].state = bubbleGrid[lsrc*nbCols+csrc].state;
        	if (bubbleGrid[ldst*nbCols+cdst].state != empty_state) 
            set_visible(bubbleGrid[ldst*nbCols+cdst]);
          set_empty(bubbleGrid[lsrc*nbCols+csrc]);
        }
      
        function movecolumn(colnumber, delta) {
        	for (i=0; i<nbLines; i++) movebubble(i, colnumber,i, colnumber+delta);
        }
        
        function connexity() {
        	// if there is a hole in the last line, the whole column is free and must be suppressed
        	for (j=0; j<nbCols; j++) {
           	if (bubbleGrid[(nbLines-1)*nbCols+j].state == empty_state) {
        			if (j<nbCols/2) {
        				for (k=j-1; k>=0; k--) movecolumn(k, +1);
        			} else {
        				for (k=j+1; k<nbCols; k++) movecolumn(k, -1);
        			}
        		}
        	}
        }
        
      "]
    } 
  ]
}
ROUTE Vover.outSFInt32 TO BUBBLE_SCRIPT.bubbleMouseOver
ROUTE Vclick.outSFInt32 TO BUBBLE_SCRIPT.bubbleOnClick
