flash on 2013-8-21

by CatHobo
An attempt to make logic gates. Left click to show menus.
♥0 | Line 938 | Modified 2013-08-28 03:24:31 | MIT License
play

ActionScript3 source code

/**
 * Copyright CatHobo ( http://wonderfl.net/user/CatHobo )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/5bq1
 */

package {
    import flash.display.Sprite;
    import flash.display.Stage;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.system.System;
    import net.hires.debug.Stats;
    
    import flash.display.Loader;
    import flash.net.URLRequest;
    import flash.system.LoaderContext;
    import flash.display.BitmapData;
    import flash.display.Bitmap;    
    import flash.geom.Rectangle;
    import flash.display.Shape;
    
    
    public class Main extends Sprite{
        public function Main():void {
            this.graphics.beginFill(0xFFFFBF);
            this.graphics.drawRect(0,0
            ,this.stage.stageWidth,this.stage.stageHeight);
            this.addChild(new Stats());
            this.stage.frameRate=20;
            ld.load(new URLRequest("http://www.clker.com/cliparts/c/7/2/0/1194989704344503149lightbulb_jon_phillips_05.svg.thumb.png")
            ,new LoaderContext(true));
            ld.contentLoaderInfo.addEventListener(Event.COMPLETE,LoadDisplay);
            LogicGUI.INIT(this); //Initialize GUI.
            ElectricityQueue.RequestBitmapDraw(
            Plug_Bitmap,new Rectangle(0,0,100,50),2);
            Wonderfl.capture_delay(20);
        }
        public static var Plug_Bitmap:Bitmap=new Bitmap(); 
        private function LoadDisplay(e:Event):void{
            //ld.content.scaleX=0.5;
            //ld.content.scaleY=0.5;
            //ld.content.x=100;
            //ld.content.y=200;
            //var cropper:Rectangle=new Rectangle(36,710,77,90);
            //ld.content.scrollRect=cropper;
            //this.addChild(ld.content);                
            var bmp_data:BitmapData=new BitmapData(
            ld.content.width,ld.content.height,true,0xFFFFFF);
            var bmp:Bitmap=new Bitmap();
            bmp_data.draw(ld);
            bmp.bitmapData=bmp_data;
            bmp.x=100;
            bmp.y=100;
            //bmp.scaleX=Electricity.SquareLength/bmp.width;
            //bmp.scaleY=Electricity.SquareLength/bmp.height; 
            this.addChild(bmp);
            //Console.Print(bmp.height);
            //Console.Print(bmp.width);
            var bmp2:Bitmap=new Bitmap();
            bmp_data.draw(ld);
            bmp2.bitmapData=bmp_data;
            bmp2.x=300;
            bmp2.y=300;
            this.addChild(bmp2); //Draw bitmaps for no reason. 
        }
        private var ld:Loader=new Loader();
        //public static var LogicGatesArray:Array=new Array();
    }
}
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.utils.Timer;
import flash.display.Stage;
import flash.display.Shape;
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.display.SimpleButton;
final class LogicGUI{
    //Static GUI class for the purpose of creating buttons
    //that execute Functions() when clicked.
    private static var AlreadyInit:Boolean=false;
    private static var GUIContainer:Sprite=new Sprite();
    private static var StageRef:Stage;
    private static var DocumentClassRef:Main;
    public static function INIT(DocumentClassRef:Main):void{
        if(AlreadyInit){throw new Error("GUI already initialized.")};
        LogicGUI.StageRef=DocumentClassRef.stage;
        LogicGUI.DocumentClassRef=DocumentClassRef;
        AlreadyInit=true;
        GUIContainer.visible=false;
        StageRef.addChild(GUIContainer);
        StageRef.addEventListener(MouseEvent.CLICK,CreateGUIListener);
    }
    private static var TimerToActivate:Timer
    =new Timer(100,1);
     private static function CreateClassObject(
     ClassObj:Class,init_x:Number,init_y:Number):Function{
        return function():void{
            /*Main.LogicGatesArray.push(
            new ClassObj(init_x,init_y));
            DocumentClassRef.addChild(
            Main.LogicGatesArray[Main.LogicGatesArray.length-1]);*/
            DocumentClassRef.addChild(new ClassObj(init_x,init_y));
        } 
    } //Factory method that creates new Electricity sprites.
    //Using it when clicking the Stage GUI buttons. 
    public static var GUIDisable:Boolean=false;
    private static function CreateGUIListener(e:MouseEvent):void{
        if(GUIDisable){
            TimerToActivate.reset();
            TimerToActivate.start();
            TimerToActivate.addEventListener(
            TimerEvent.TIMER_COMPLETE,
            function(e:TimerEvent):void
            {GUIDisable=false;});
            return; //This code is still broken.
        } 
        if(e.target is Stage){
            var menu_items:Array=[
            [CreateClassObject(InputSprite,e.stageX,e.stageY),null,[],"Create InputSprite"],
            [CreateClassObject(BufferGate,e.stageX,e.stageY),null,[],"Create BufferGate"],
            [CreateClassObject(NOTGate,e.stageX,e.stageY),null,[],"Create NOTGate"],
            [CreateClassObject(ANDGate,e.stageX,e.stageY),null,[],"Create ANDGate"],
            [CreateClassObject(ORGate,e.stageX,e.stageY),null,[],"Create ORGate"],
            [CreateClassObject(XORGate,e.stageX,e.stageY),null,[],"Create XORGate"],
            [CreateClassObject(NANDGate,e.stageX,e.stageY),null,[],"Create NANDGate"],
            [CreateClassObject(NORGate,e.stageX,e.stageY),null,[],"Create NORGate"],
            [CreateClassObject(XNORGate,e.stageX,e.stageY),null,[],"Create XNORGate"]
            ] //Creates new sprite objects and make them appear.
             
            for each(var menu_item:Object in menu_items){
                FunctionsArray.push(menu_item[0]);
                ObjectRefArray.push(menu_item[1]);
                ArgumentsArray.push(menu_item[2]);
                ButtonNameArray.push(menu_item[3]);
            } 
            GUIContainer.visible=true;
            GUIContainer.graphics.clear();
            CreateButtons();
        }else if(e.target is GUIInterface){
            var MenuItemArray:Array=e.target.ImplementMenuItems();
            for each(var menu_item2:Object in MenuItemArray){
                FunctionsArray.push(menu_item2[0]);
                ObjectRefArray.push(menu_item2[1]);
                ArgumentsArray.push(menu_item2[2]);
                ButtonNameArray.push(menu_item2[3]);
            }
            GUIContainer.visible=true;
            GUIContainer.graphics.clear();
            CreateButtons();
        }
        GUIContainer.x
            =(e.stageX-10+GUIContainer.width<StageRef.stageWidth
            ?e.stageX-10
            :StageRef.stageWidth-GUIContainer.width);
        GUIContainer.y
            =(e.stageY-10+GUIContainer.height<StageRef.stageHeight
            ?e.stageY-10
            :StageRef.stageHeight-GUIContainer.height+5);
            //Set x and y such that the GUI is always "on-screen"
            //and it doesn't make the GUI "off-bounds" and "unreadable".
        GUIContainer.graphics.beginFill(0x000000);
        GUIContainer.graphics.drawRoundRect(-3,-3
            ,GUIContainer.width+6,GUIContainer.height+6,20,20); 
            //Black round rectangle to hold the buttons.
        GUIContainer.addEventListener(
            MouseEvent.ROLL_OUT,HideGUIListener);
        GUIContainer.addEventListener(
            MouseEvent.CLICK,HideGUIListener);
        
        
    }
    private static function HideGUIListener(e:MouseEvent):void{
       //If clicking a SimpleButton, or hovering out the GUI...
        GUIContainer.visible=false;
        for(var i:int=0
        ;i<GUIContainer.numChildren;i++){
            GUIContainer.getChildAt(i) 
            .removeEventListener(
            MouseEvent.CLICK,ButtonListeners[i]);
            //Remove event listeners
            //for each button, the complicated way.
        }
        while (GUIContainer.numChildren > 0) {
            GUIContainer.removeChildAt(0);
        } //Remove-all-children loop.
        ClearButtonArrays();
        GUIContainer.removeEventListener(
        MouseEvent.ROLL_OUT,HideGUIListener);
        GUIContainer.removeEventListener(
        MouseEvent.CLICK,HideGUIListener);
    }
    private static var FunctionsArray:Vector.<Function>
    =new Vector.<Function>();
    private static var ObjectRefArray:Array=new Array();
    private static var ArgumentsArray:Array=new Array();
    private static var ButtonNameArray:Vector.<String>
    =new Vector.<String>();
    private static var ButtonListeners:Vector.<Function>
    =new Vector.<Function>();
    private static function CreateButtons():void{
        for(var i:int=0;i<FunctionsArray.length;i++){
            GUIContainer.addChild(
            CreateAButton(FunctionsArray[i]
            ,ObjectRefArray[i]
            ,ArgumentsArray[i]
            ,ButtonNameArray[i]));
        }
    }
    private static function ClearButtonArrays():void{
        FunctionsArray.length=0;
        ArgumentsArray.length=0;
        ButtonNameArray.length=0;
        ButtonListeners.length=0;
        ObjectRefArray.length=0;
    }
    private static function CreateAButton(
    ButtonFunction:Function,ObjRef:Object
    ,ButtonArguments:Array,ButtonName:String):SimpleButton{
        var sb:SimpleButton=new SimpleButton(
        CreateAButtonFrame(0x7F7F7F,ButtonName),//Un-highlighted
        CreateAButtonFrame(0x7F7FFF,ButtonName),//highlighted
        CreateAButtonFrame(0xFF7F00,ButtonName),//Clicked
        CreateAButtonFrame(0xFFFFFF,ButtonName));//Area to click?
        var WhenClickedCallThis:Function=
        function(e:MouseEvent):void{
            ButtonFunction.apply(ObjRef,ButtonArguments);
        } //Anonymous listener that will call the function
        //with the proper arguments used.
        ButtonListeners.push(WhenClickedCallThis);
        sb.addEventListener(
        MouseEvent.CLICK,WhenClickedCallThis);
        return sb;
    }
    private static function CreateAButtonFrame(
    color:uint,message:String="Call Function()"):Sprite{
        var spr:Sprite=new Sprite();
        //For button graphics and container for TextField.
        var tf:TextField=new TextField();
        tf.defaultTextFormat=new TextFormat(
        "Verdana",19,0xFFFFFF,true,true);
        tf.width=250;
        tf.wordWrap=true;
        tf.text=message;
        tf.width=tf.textWidth+5;
        //Set width smaller if there's little text.
        tf.autoSize=TextFieldAutoSize.LEFT;
        spr.addChild(tf);
        spr.graphics.lineStyle(2,0x000000);
        spr.graphics.beginFill(color);
        spr.y=GUIContainer.height;
        //For each new button, move the button down.
        spr.graphics.drawRoundRect(0,0
        ,tf.width,tf.height,20,20);
        return spr;
    }
} 


interface GUIInterface{
    function ImplementMenuItems():Array;
    /*Implements a multi-dimensional array that returns
the form [[Function object,object reference,[Arguments Array],ButtonName],
[Function object,object reference,[Arguments Array],ButtonName],
[Function object,object reference,[Arguments Array],ButtonName],
...]
so that the GUI can call sprite functions by clicking buttons.*/
}


import flash.display.Sprite;
    //Attempting to see how inheritance works.
class Entity extends Sprite{
    private static var _EntitiesCreated:int=0;
    public static function EntitiesCreated():int{
        return _EntitiesCreated;
    }
    protected var ID:int=++_EntitiesCreated;
    protected var _Type:String="Type N/A";
    public function get Type():String{
        return _Type;
        
    }
    public function GetID():int{
        return ID;
    }
    override public function toString():String{
        return super.toString()+" ID #"+ID;
        //toString with just ID # added.
    }
    public function Entity(init_x:Number=0,init_y:Number=0):void{
        x=init_x;
        y=init_y;
    }
}
import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.Sprite;
import flash.utils.Dictionary;
class WireSprite extends Entity implements GUIInterface{
    public function ImplementMenuItems():Array{
        return [
            [RemoveWireObject,this,[],"Remove Wire"]
        ]
    }
    public function WireSprite(
    e_in:Electricity,e_in_node:Sprite
    ,e_out:Electricity,e_out_node:Sprite,input_num:int):void{
        _Type="WireSprite";
        this.buttonMode=true;
        function UpdateGraphics(e:Event):void{
            try{
            graphics.clear();
            graphics.lineStyle(5,
            e_out.IsPowerON()?0x3FFF3F:0xFF3F3F); 
            graphics.moveTo(e_out_node.x+6
            ,e_out_node.y);
            graphics.lineTo(e_in.x+e_in_node.x-e_out.x-6
            ,e_in.y+e_in_node.y-e_out.y);
            //Console.Print("This will appear until you delete wires.");
            }catch(E:Error){
                RemoveWireObject();
            }
        }
        function WhenClickedRemoveWire(e:MouseEvent=null):void{
            if(CalledOnce){return;}
            CalledOnce=true;
            //Console.Print("Deleted wire name \""+name+"\"");
            e_out.AsOutputDisconnectInput(e_in,input_num);
            e_in.Run(); //Update inputs as Output is disconnected.
            RemoveSelf();
            //removeEventListener(MouseEvent.CLICK,WhenClickedRemoveWire);
            removeEventListener(Event.ENTER_FRAME,UpdateGraphics);
        }
        //this.addEventListener(MouseEvent.CLICK,WhenClickedRemoveWire);
        this.addEventListener(Event.ENTER_FRAME,UpdateGraphics);
        this.RemoveWireObject=WhenClickedRemoveWire;
    }
    
    private var CalledOnce:Boolean=false;
    public var RemoveWireObject:Function;
        //Function closure used to "delete" 
        //the Event listeners and the object.
    private function RemoveSelf():void{
        this.parent.removeChild(this);
        //Remove self using the Electricity class object (parent)
        //where it is added.
    }
}

import flash.display.Loader;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.display.BitmapData;
import flash.display.Bitmap;    
import flash.geom.Rectangle;
import flash.display.Shape;
import flash.events.Event;
import flash.display.Sprite;
final class ElectricityQueue{
    //Custom static class for the purpose of calling the function Run()
    //of each ElectricityEvent object (When DispatchToObj is called),
    //and preloading images for the gates.
    private static var EveryFrameCall:Sprite=new Sprite();
    //private static var ld:Loader=new Loader();
    //private static var LoadedComplete:Boolean=false;
    private static var LoaderArray:Vector.<Loader>
    =new Vector.<Loader>();
    private static var LoadedCompleteArray:Vector.<Boolean>
    =new Vector.<Boolean>();
    //New code that can load multiple pictures and can wait when
    //one of the pictures are still loading.
    {
        EveryFrameCall.addEventListener(Event.ENTER_FRAME,StartDispatch);
        EveryFrameCall.addEventListener(Event.ENTER_FRAME,PictureLoadedDispatch);
        
        LoaderArray.push(new Loader(),new Loader(),new Loader());
        LoadedCompleteArray.push(false,false,false);
        LoaderArray[0].load(new URLRequest(
        "http://openclipart.org/image/800px/svg_to_png/12885/nobody_Digital_logic_gates.png")
        ,new LoaderContext(true));
        LoaderArray[0].contentLoaderInfo.addEventListener(Event.COMPLETE,LoadDisplay);
        LoaderArray[1].load(new URLRequest(
        "http://www.clker.com/cliparts/8/7/1/6/1351019548860867760Power%20Symbol.svg.thumb.png")
        ,new LoaderContext(true));
        
        LoaderArray[1].contentLoaderInfo.addEventListener(Event.COMPLETE,LoadDisplay);
        
        LoaderArray[2].load(new URLRequest(
        "http://www.clker.com/cliparts/F/0/4/Y/f/a/power-cable-us-th.png")
        ,new LoaderContext(true));
        LoaderArray[2].contentLoaderInfo.addEventListener(Event.COMPLETE,LoadDisplay);
         
        /*ld.load(new URLRequest(
        "http://openclipart.org/image/800px/svg_to_png/12885/nobody_Digital_logic_gates.png")
        ,new LoaderContext(true));
        ld.contentLoaderInfo.addEventListener(Event.COMPLETE,LoadDisplay);*/
    } //Static Initializer.
    private static function LoadDisplay(e:Event):void{
        //LoadedComplete=true;
        for(var i:int=0;i<LoadedCompleteArray.length;i++){
            if(e.target==LoaderArray[i].contentLoaderInfo){
                LoadedCompleteArray[i]=true;
            }
        }
        e.target.removeEventListener(Event.COMPLETE,LoadDisplay);
        //Allow PictureLoadedDispatch to process drawing cropped bitmaps.
    }
    private static function PictureLoadedDispatch(e:Event):void{
        
        //if(!LoadedComplete){return;}
        if(BitmapToDrawQueue.length==0){return;}
        
        var InfiniteLoop:int=0
        while(BitmapToDrawQueue.length!=0){
            if(++InfiniteLoop>=1000)
            {throw new Error("Infinite loop.");}
            if(!LoadedCompleteArray[PictureNumberQueue[0]]){
                return;
            }//If picture number is still loading, don't go to next code.
            var picture_i:int=PictureNumberQueue.shift();
            var bmp:Bitmap=BitmapToDrawQueue.shift();
            var crop_rectangle:Rectangle=CropQueue.shift();
            //Pop bitmap reference and rectangle from queue.
             
            //ld.content.scrollRect=crop_rectangle;
            LoaderArray[picture_i].content.scrollRect=crop_rectangle;
            
            var bmp_data:BitmapData=new BitmapData(
            crop_rectangle.width,crop_rectangle.height,true,0xFFFFFF);
            
            //bmp_data.draw(ld); //Draw from loaded picture. 
            bmp_data.draw(LoaderArray[picture_i]);
            
            
            bmp.bitmapData=bmp_data; 
            bmp.x+=Electricity.BorderLength;
            bmp.y+=Electricity.BorderLength;
            bmp.scaleX
            =(Electricity.SquareLength-Electricity.BorderLength*2)/bmp.width;
            bmp.scaleY
            =(Electricity.SquareLength-Electricity.BorderLength*2)/bmp.height;
            //Make the size the same as the size of the gates.
        }
    }
    
    public static function RequestBitmapDraw(
    bitmap_ref:Bitmap,crop_rectangle:Rectangle,pic_num:int=0):void{
        BitmapToDrawQueue.push(bitmap_ref);
        CropQueue.push(crop_rectangle);
        PictureNumberQueue.push(pic_num);
    } //Push bitmap reference so that it will be drawn when
    //the picture is loaded, as well as the "crop" rectangle
    //that will get the x, y, width, and height it will draw.
    private static var BitmapToDrawQueue:Vector.<Bitmap>
    =new Vector.<Bitmap>();
    private static var CropQueue:Vector.<Rectangle>
    =new Vector.<Rectangle>();
    private static var PictureNumberQueue:Vector.<int>
    =new Vector.<int>();
    
    private static function StartDispatch(e:Event):void{
        if(ElectricitySpriteQueue.length==0){return;}
        //var Count:uint=0;
        var InfiniteLoop:int=0
        var PreviousLength:int=ElectricitySpriteQueue.length;
        //To prevent an infinite loop, get previous length
        //of ElectricitySpriteQueue, as calling it in
        //the while loop is a problem (it keeps changing).
        while(PreviousLength!=0){
            if(++InfiniteLoop>=1000)
            {throw new Error("ElectricitySpriteQueue too big.");}
            var obj:Electricity=ElectricitySpriteQueue.shift();
            obj.Run();
            //++Count;
            --PreviousLength; 
            //Console.Print(obj.Type+"("+obj.GetID()+")");
        }
        
        
        //Console.Print(Count+" have been called to Run() in queue.");
        //Count debug to see if there's a problem with this Event class. 
    }
    public static var ElectricitySpriteQueue:Array=new Array();
    //If multiple Electricity sprites calls DispatchToObj,
    //the queue will try to add all of them to call Run() above.
    public static function DispatchToObj(E:Electricity):void{
        for each(var obj:Electricity in ElectricitySpriteQueue){
            if(E==obj){return;} //No duplicates are necessary.
        }
        ElectricitySpriteQueue.push(E);
    }
    
}

import flash.geom.Rectangle;
final class LOGIC_GATE_ENUMS{
    public static var BUFFER:Rectangle=new Rectangle(36,710,77,90);
    public static var NOT:Rectangle=new Rectangle(36,710,96,90);
    public static var AND:Rectangle=new Rectangle(313,0,130,110);
    public static var NAND:Rectangle=new Rectangle(313,0,145,110);
    public static var OR:Rectangle=new Rectangle(313,235,145,110);
    public static var NOR:Rectangle=new Rectangle(313,235,162,110);
    public static var XOR:Rectangle=new Rectangle(313,460,165,120);
    public static var XNOR:Rectangle=new Rectangle(313,460,183,120);
}


import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.SimpleButton;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
class NodeOutput extends Sprite implements GUIInterface{
    public function ImplementMenuItems():Array{
        return [
        [ConnectTo,this,[],"Connect Output To"]
        ]
    }
    private function ConnectTo():void{
        /*Console.Print("Connecting "+(this.parent as Electricity).Type
        +"("+(this.parent as Electricity).GetID()+") to..."); */
        this.root.stage.addEventListener(
        MouseEvent.CLICK,ClickListener); //Using stage for mouse clicks.
        (this.root as Main).addChild(Main.Plug_Bitmap);
        Main.Plug_Bitmap.x=this.parent.x+this.x;
        Main.Plug_Bitmap.y=this.parent.y+this.y;
    }
    private function ClickListener(e:MouseEvent):void{
        if(e.target is NodeInput){
            (this.parent as Electricity).AsOutputConnectToInput(
            e.target.parent as Electricity,
            e.target.GetNodeInputID()); //Connect wire. 
        }else if(!(e.target is SimpleButton)){
            //Console.Print("Cancelled.");
        }
        //Click anywhere to cancel,
        //but click an input node to connect.
        LogicGUI.GUIDisable=true;
        if(!(e.target is SimpleButton)||e.target is NodeInput){
            this.root.stage.removeEventListener(
            MouseEvent.CLICK,ClickListener);
            (this.root as Main).removeChild(Main.Plug_Bitmap);
        }
    }
} 
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.display.SimpleButton;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
class NodeInput extends Sprite implements GUIInterface{
    public function ImplementMenuItems():Array{
        return [
        [ConnectTo,this,[],"Connect Input "+NodeInputID+" To"]
        ]
    } 
    public function NodeInput(Init_ID:int):void{
        NodeInputID=Init_ID;
    }
    private var NodeInputID:int;
    public function GetNodeInputID():int{
        return NodeInputID;
    }
    private function ConnectTo():void{
        /*Console.Print("Connecting "+(this.parent as Electricity).Type
        +"("+(this.parent as Electricity).GetID()+") to..."); */
        this.root.stage.addEventListener(
        MouseEvent.CLICK,ClickListener);
        //Using stage for mouse clicks.
        (this.root as Main).addChild(Main.Plug_Bitmap);
        Main.Plug_Bitmap.x=this.parent.x+this.x;
        Main.Plug_Bitmap.y=this.parent.y+this.y;
    } //Then click a Node Input sprite to connect gates.
    private function ClickListener(e:MouseEvent):void{
        if(e.target is NodeOutput){
            (e.target.parent as Electricity).AsOutputConnectToInput(
            this.parent as Electricity,NodeInputID); //Connect wire. 
        }else if(!(e.target is SimpleButton)){
            //Console.Print("Cancelled.");
        }
        //Click anywhere to cancel, 
        //but click an input node to connect.
        LogicGUI.GUIDisable=true;
        if(!(e.target is SimpleButton)||e.target is NodeOutput){
            this.root.stage.removeEventListener(
            MouseEvent.CLICK,ClickListener);
            (this.root as Main).removeChild(Main.Plug_Bitmap);
        }
    }
} 
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.ColorTransform;
import flash.geom.Point;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.display.Bitmap;    
import flash.geom.Rectangle;
class Electricity extends Entity{
    public static const SquareLength:Number=48;
    public static const BorderLength:Number=4;
    public function Electricity(
    init_power:Boolean=false,init_x:Number=0
    ,init_y:Number=0,max_inputs:int=0,type:String="Type N/A"):void{
        super(init_x,init_y);
        _Type=type;
        PowerON=init_power;
        if(this is InputSprite){
            DrawBodyColor(init_power?0x7F7FFF:0xBF3F3F);
            //Fill as "on" or "off".
        }else{
            DrawBodyColor(0x7F7F7F); //Fill as "disconnected". 
        }
        CreateNode(OutputNode,this.width,this.height/2-1);
        MaxInputs=max_inputs;
        
        if(MaxInputs==0){
            InputNodes=null;
        }else{ 
            for(var i:int=0;i<MaxInputs;i++){
                InputNodes.push(new NodeInput(i+1));
                CreateNode(InputNodes[i],0
                ,(this.height/(MaxInputs-1+2))*(i+1)-1);
            } //Create variable number of input nodes.
        }
        var UseCrop:Rectangle; //To crop bitmap pictures with.
        var UsePictureNumber:int; //Load specific picture number.
        switch(_Type){
            case "BufferGate":
                UseCrop=LOGIC_GATE_ENUMS.BUFFER;
                UsePictureNumber=0;
                break;
            case "NOTGate":
                UseCrop=LOGIC_GATE_ENUMS.NOT;
                UsePictureNumber=0;
                break;
            case "ANDGate":
                UseCrop=LOGIC_GATE_ENUMS.AND;
                UsePictureNumber=0;
                break;
            case "ORGate":
                UseCrop=LOGIC_GATE_ENUMS.OR;
                UsePictureNumber=0;
                break;
            case "XORGate":
                UseCrop=LOGIC_GATE_ENUMS.XOR;
                UsePictureNumber=0;
                break;
            case "NANDGate":
                UseCrop=LOGIC_GATE_ENUMS.NAND;
                UsePictureNumber=0;
                break;
            case "NORGate":
                UseCrop=LOGIC_GATE_ENUMS.NOR;
                UsePictureNumber=0;
                break;
            case "XNORGate":
                UseCrop=LOGIC_GATE_ENUMS.XNOR;
                UsePictureNumber=0;
                break; 
            case "InputSprite":
                UseCrop=new Rectangle(0,0,97,99);
                UsePictureNumber=1;
        }
        
        if(UseCrop){ //If UseCrop rectangle isn't null. 
            ElectricityQueue.RequestBitmapDraw(BitmapPicture
            ,UseCrop,UsePictureNumber);
            this.addChild(BitmapPicture);
        }
        
        //Temporary code to see the ID of each Sprite.
        /*ID_T.text="ID #"+String(ID);
        ID_T.selectable=false;
        ID_T.autoSize=TextFieldAutoSize.LEFT;
        this.addChild(ID_T);*/
    }
    //private var ID_T:TextField=new TextField();
    private function CreateNode(spr_ref:Sprite,
    init_x:Number,init_y:Number):void{
        spr_ref.x=init_x;
        spr_ref.y=init_y;
        spr_ref.graphics.beginFill(PowerON?0x007F00:0x7F0000);
        spr_ref.graphics.drawCircle(0,0,6);
        spr_ref.buttonMode=true;
        this.addChild(spr_ref);
    }
    protected function DrawBodyColor(color:uint):void{
        this.graphics.beginFill(color);
        this.graphics.drawRoundRect(0,0,SquareLength,SquareLength,20,20);
    }

    private var BitmapPicture:Bitmap=new Bitmap();
    protected var OutputNode:NodeOutput=new NodeOutput();
    protected var InputNodes:Vector.<NodeInput>=new Vector.<NodeInput>();
    
    protected var PowerON:Boolean=false;
    public function IsPowerON():Boolean{
        return PowerON;
    }
    protected var MaxInputs:int;
    protected var InputsConnected:Boolean=false;
    public function IsInputsConnected():Boolean{
        return InputsConnected;
    }
    public function Run():void{}
        //Will be implemented by other classes.
        
    
    //protected var InputDictionary:Dictionary=new Dictionary(true);
    //protected var OutputDictionary:Dictionary=new Dictionary(true);
    
    protected var InputArray:Array=new Array();
    protected var OutputArray:Array=new Array();

    public function AsOutputConnectToInput(
    e_in:Electricity,input_num:int=1):void{
        if(!OutputArray){
            throw new Error(
            "Object "+this.Type+" doesn't have Outputs.");
        }

        //this.OutputDictionary[e_in]=null;
        //e_in.InputDictionary[this]=input_num;
        this.OutputArray.push(e_in);
        var old_output:Electricity=e_in.InputArray[input_num-1];
        if(old_output){
            old_output.AsOutputDisconnectInput(e_in,input_num);
        } //Delete old input connections if there are any.
        e_in.InputArray[input_num-1]=this;
        var w_s:WireSprite=new WireSprite(
        e_in,e_in.InputNodes[input_num-1]
        ,this,this.OutputNode,input_num);
        w_s.name=GetWireName(this,e_in,input_num);
        //Console.Print("Added wire name \""+w_s.name+"\"");
        //name used to delete specific wires.
        this.addChildAt(w_s,0);       
        this.CallRunForOthers();
    }
    private function GetWireName(
    e_out:Electricity,e_in:Electricity,input_num:int):String{
        return "Output("+e_out.GetID()+")"+
        "Input("+e_in.GetID()+","+input_num+")";
    } //input_num from 1 to ...
    
    public function AsOutputDisconnectInput(e_in:Electricity,
    input_num:int):void{
        if(!OutputArray){
            throw new Error(
            "Object "+this.Type+" doesn't have Outputs.");
        }
        var i:int;
        //delete e_in.InputDictionary[this];
        //delete this.OutputDictionary[e_in];
        var stupid_infinite_loop:int=0;
        for(i=0;i<this.OutputArray.length;i++){
            if(this.OutputArray[i]==e_in){
                //If we found the respective Electricity object,
                //cycle through the other Array,
                //to find the specific input number
                //where it was connected to as Output to disconnect.
                //Ex: (Disconnecting ID #1 output connecting to ID #2's
                //input #2, and disconnecting ID #2's input #2
                //at the same time. If there's an ID #2 input #1,
                //don't disconnect that one.) 
                for(var j:int=0;j<e_in.InputArray.length;j++){
                    if(e_in.InputArray[j]==this&&input_num-1==j){
                        e_in.InputArray[j]=null;
                        //this.OutputArray[i]=null;
                        OutputArray.splice(i,1);
                        //Fixing OutputArray so that
                        //an item is "popped" out at some index.
                        
                        var FoundWire:WireSprite=(this.getChildByName(
                            GetWireName(this,e_in,input_num)) as WireSprite);
                        if(FoundWire){
                            FoundWire.RemoveWireObject();
                        } //Delete WireSprite.
                    }
                    if(++stupid_infinite_loop>=1000){
                        throw new Error("infinite loop2.");
                    }
                }
            }
            if(++stupid_infinite_loop>=1000){
                throw new Error("infinite loop.");
            }
            
        }
        this.CallRunForOthers();
    }
    public function DisconnectAllWires():void{
        //Console.Print("Attempting to delete all wires for ID #"+this.ID);
        var stop_infinite_loop:int=0;
        while(this.OutputArray.length!=0){
            //Delete output connections for this object.
            if(++stop_infinite_loop>1000){
                throw new Error("Infinite loop.");
            }
            //Using the first item to delete (this.OutputArray[0]), find
            //the index so that the input number (j+1)
            //is found to use the AsOutputDisconnectInput function. 
            var FirstItem:Electricity=this.OutputArray[0];
            for(var j:int=0;j<FirstItem.InputArray.length;j++){
                if(FirstItem.InputArray[j]==this){
                    this.AsOutputDisconnectInput(FirstItem,j+1);
                    break;
                }
            }
        }
        for(var i:int=0;i<this.InputArray.length;i++){
            var output_obj:Electricity=this.InputArray[i] as Electricity;
            if(output_obj){
                output_obj.AsOutputDisconnectInput(this,i+1);
            } //Delete input connections for this object.
        }
    }
        
    public function CallRunForOthers():void{
        /*for(var output_obj:Object in OutputDictionary){
            ElectricityQueue.DispatchToObj(output_obj as Electricity);
        } //Call other Electricity object's Run().*/
        for(var i:int=0;i<this.OutputArray.length;i++){
            if(this.OutputArray[i]){
                ElectricityQueue.DispatchToObj(this.OutputArray[i]);
            } //Dispatch only defined Electricity objects.
            
        }
    }
    
    override public function toString():String{
        return super.toString()+", Power ("+(PowerON?"ON)":"OFF)");
    }
    protected function GetStringInputsAndOutputs():String{
        var TempString:String="";
        var HasOutputs:Boolean=false;
        /*for(var obj_output:Object in OutputDictionary){
            TempString+=obj_output.Type+" (ID #"+obj_output.GetID()
            +")("+(obj_output.PowerON?"ON":"OFF")+"),";
            HasOutputs=true;
        }//Gets the outputs it is connected to.*/
        var i:int;
        for(i=0;i<this.OutputArray.length;i++){
            if(OutputArray[i]){
                TempString+=OutputArray[i].Type+" (ID #"
                +OutputArray[i].GetID()
                +")("+(OutputArray[i].PowerON?"ON":"OFF")+"),";
                HasOutputs=true;
            }
        }
        if(!HasOutputs){
            TempString+=" N/A...";
        }else{
            TempString+="..."
        }
        TempString+=" Max Inputs = "+MaxInputs+"...";
        /*for(var obj_input:Object in InputDictionary){
            TempString+=" Input #"+InputDictionary[obj_input]
            +"("+obj_input.Type+" ID #"+obj_input.GetID()
            +")("+(obj_input.PowerON?"ON":"OFF")+"),";
        }*/
        for(i=0;i<InputArray.length;i++){
            if(InputArray[i]){
                TempString+=" Input #"+(i+1)
                +"("+InputArray[i].Type+" ID #"+InputArray[i].GetID()
                +")("+(InputArray[i].PowerON?"ON":"OFF")+"),";
            }
        }
        return TempString;
    } //To add to toString in NonWireSprite class.
    protected function ChangeColorNode(node_ref:Sprite,b:Boolean):void{
        var ct:ColorTransform=new ColorTransform();
        ct.color=(b?0x007F00:0x7F0000);
        node_ref.transform.colorTransform=ct;    
    }
    protected function DragThisSprite():void{
        this.addEventListener(MouseEvent.MOUSE_MOVE,StartDrag);
        this.dispatchEvent(
        new MouseEvent(MouseEvent.MOUSE_MOVE
        ,false,false));
        //Dispatch a custom mouse event.
        Old_z=this.parent.getChildIndex(this);
        this.parent.addChild(this); //Move to front using document class.
    }
    private var Old_z:int;
    private function StartDrag(e:MouseEvent):void{
        this.x=this.parent.mouseX-SquareLength/2;
        this.y=this.parent.mouseY-SquareLength/2;
        //Code to move the sprite in the middle. 
        //Excluding the wires using the 
        //SquareLength constant. 
        e.updateAfterEvent();
        this.parent.addEventListener(
        MouseEvent.CLICK,StopDrag);
        LogicGUI.GUIDisable=true;
    }
    private function StopDrag(e:MouseEvent):void{
        this.root.removeEventListener(
        MouseEvent.CLICK,StopDrag);
        this.removeEventListener(MouseEvent.MOUSE_MOVE,StartDrag);
        this.parent.addChildAt(this,Old_z);
        //Put back in old position, because wires block the input nodes.
    }
    public function DeleteSprite():void{
        DisconnectAllWires();
        this.addEventListener(
        Event.ENTER_FRAME,FinallyDeleteSprite);
    }
    
    private function FinallyDeleteSprite(e:Event):void{
        if(!this.InputsConnected||this is InputSprite){
            this.removeEventListener(
            Event.ENTER_FRAME,FinallyDeleteSprite);
            this.parent.removeChild(this);
        }
    }
    protected var SharedMenuItems:Array=[
        [DragThisSprite,this,[],"Drag This Sprite"],
        [DeleteSprite,this,[],"Delete This Sprite"],
        [DisconnectAllWires,this,[],"Disconnect All Wires"]
    ] //Functions for GUI Interface buttons.
}
import flash.display.Sprite;
class NonWireSprite extends Electricity{
    public function NonWireSprite(
    init_power:Boolean=false,init_x:Number=0,
    init_y:Number=0,max_inputs:int=0,type:String="Type N/A"):void{
        super(init_power,init_x,init_y,max_inputs,type);
    }
    override public function toString():String{
        var TempString:String=", Outputs To object(s):";
        TempString+=GetStringInputsAndOutputs();
        return super.toString()+", position (x="
        +x+", y="+y+")"+TempString;
        
    }
}
//WireSprite extends Electricity is moved as it extends Entity.
import flash.display.Sprite;
class InputSprite extends NonWireSprite implements GUIInterface{
    public function ImplementMenuItems():Array{
        return super.SharedMenuItems.concat([
        [SetPower,this,[!super.PowerON],
            "Turn Power "+(!super.PowerON?"On":"Off")]
        ])
    }
    public function InputSprite(
    init_x:Number=0,init_y:Number=0,init_power:Boolean=false):void{
        super(init_power,init_x,init_y,0,"InputSprite");
        InputsConnected=true;
    }
    public function SetPower(b:Boolean):void{
        super.PowerON=b;
        CallRunForOthers();
        ChangeColorNode(OutputNode,b);
        DrawBodyColor(b?0x7F7FFF:0xBF3F3F); 
    }
}

import flash.display.Sprite;
class LogicGate extends NonWireSprite{
    public function LogicGate(init_x:Number=0,init_y:Number=0,
    start_bool:Boolean=false,
    use_inverse:Boolean=false,max_inputs:int=0,type:String="Type N/A"):void{
        super(false,init_x,init_y,max_inputs,type);
        this.start_bool=start_bool;
        this.use_inverse=use_inverse;
    }
    
    private var start_bool:Boolean;
    private var use_inverse:Boolean;
    override public function Run():void{
        var obj_count:int=0;
        var bool_result:Boolean=start_bool;        
        /*for(var obj_input:Object in InputDictionary){
            try{
                if(!obj_input.IsInputsConnected()){
                    this.PowerON=false;
                    ChangeColorNode(OutputNode,false);
                    this.InputsConnected=false;
                    this.CallRunForOthers();
                    return;
                } //If InputsConnected is false in one object,
                //make this as well and turn the power OFF.
                obj_count++;
                bool_result=FunctionToUse(
                    bool_result,obj_input.IsPowerON());
                ChangeColorNode(
                InputNodes[InputDictionary[obj_input]-1]
                ,obj_input.IsPowerON());
            }catch(E:Error){
                delete InputDictionary[obj_input];
                this.PowerON=false;
                ChangeColorNode(OutputNode,false);
                this.InputsConnected=false;
                this.CallRunForOthers();
                return;
            }
        }*/
        var InputSpriteConnected:Boolean=false;
            //Assume we're disconnected.
        for(var i:int=0;i<InputArray.length;i++){
            if(!InputArray[i]){
                this.PowerON=false;
                ChangeColorNode(InputNodes[i],false);
                continue; //If null/undefined, don't count this.
            }
            if(InputArray[i].IsInputsConnected()
            &&InputArray[i]!=this){
                if(!(InputArray[i] is InputSprite)){
                    var PreviousInputsConnected:Boolean=true;
                    for(var j:int=0;
                    j<InputArray[i].InputArray.length;j++){
                        if(!InputArray[i].InputArray[j].IsInputsConnected()){
                            PreviousInputsConnected=false;
                            break;
                        }
                    }
                    if(PreviousInputsConnected){
                        this.PowerON=true; 
                        ChangeColorNode(InputNodes[i],true);
                        this.InputsConnected=true;
                        InputSpriteConnected=true;
                    }
                }else{
                        this.PowerON=true;
                        ChangeColorNode(InputNodes[i],true);
                        this.InputsConnected=true;
                        InputSpriteConnected=true;
                } 
            }
            
            
            
            obj_count++;
            bool_result=FunctionToUse(
                    bool_result,InputArray[i].IsPowerON()); 
            ChangeColorNode(
            InputNodes[i],InputArray[i].IsPowerON());
        }
        
        if(obj_count!=MaxInputs||!InputSpriteConnected){
            this.PowerON=false;
            ChangeColorNode(OutputNode,false);
            this.InputsConnected=false;
            DrawBodyColor(0x7F7F7F);
            this.CallRunForOthers();
            return;
        }
        //All inputs should be connected for the logic gate
        //to turn ON, or it will be OFF.
        var bool_result2:Boolean
        =(use_inverse?(!bool_result):bool_result);
        this.PowerON=bool_result2;
        ChangeColorNode(OutputNode,bool_result2);
        this.InputsConnected=true;
        DrawBodyColor(bool_result2?0x7F7FFF:0xBF3F3F);
        this.CallRunForOthers();
    }
    protected var FunctionToUse:Function
    =function(a:Boolean,b:Boolean):Boolean{return true;};
        //Subclasses will determine which function to use below. 
    
    protected function UseAND(a:Boolean,b:Boolean):Boolean{
        return a && b;
    }
    protected function UseOR(a:Boolean,b:Boolean):Boolean{
        return a || b;
    }
    protected function UseXOR(a:Boolean,b:Boolean):Boolean{
        
        return (a && !b)||(!a && b);
    }    
}
import flash.display.Sprite;
class BufferGate extends LogicGate implements GUIInterface{
    public function ImplementMenuItems():Array{
        return SharedMenuItems;
    }
    public function BufferGate(init_x:Number=0,init_y:Number=0):void{
        super(init_x,init_y,false,false,1,"BufferGate");
        FunctionToUse=UseOR;
    }
} //Truth Table (true passes true and false passes false)

import flash.display.Sprite;
class NOTGate extends LogicGate implements GUIInterface{
    public function ImplementMenuItems():Array{
        return SharedMenuItems;
    }
    public function NOTGate(init_x:Number=0,init_y:Number=0):void{
        super(init_x,init_y,false,true,1,"NOTGate");
        FunctionToUse=UseOR;
    }
} //Truth Table (true passes false and false passes true)

import flash.display.Sprite;
class ANDGate extends LogicGate implements GUIInterface{
    public function ImplementMenuItems():Array{
        return SharedMenuItems;
    }
    public function ANDGate(init_x:Number=0,init_y:Number=0,
    max_inputs:int=2):void{
        super(init_x,init_y,true,false,(max_inputs>=2?max_inputs:2),"ANDGate");
        FunctionToUse=UseAND;
    }
}

import flash.display.Sprite;
class ORGate extends LogicGate implements GUIInterface{
    public function ImplementMenuItems():Array{
        return SharedMenuItems;
    }
    public function ORGate(init_x:Number=0,init_y:Number=0,
    max_inputs:int=2):void{
        super(init_x,init_y,false,false,(max_inputs>=2?max_inputs:2),"ORGate");
        FunctionToUse=UseOR;
    }
}

import flash.display.Sprite;
class XORGate extends LogicGate implements GUIInterface{
    public function ImplementMenuItems():Array{
        return SharedMenuItems;
    }
    public function XORGate(init_x:Number=0,init_y:Number=0,
    max_inputs:int=2):void{
        super(init_x,init_y,false,false,(max_inputs>=2?max_inputs:2),"XORGate");
        FunctionToUse=UseXOR;
    }
}

import flash.display.Sprite;
class NANDGate extends LogicGate implements GUIInterface{
    public function ImplementMenuItems():Array{
        return SharedMenuItems;
    }
    public function NANDGate(init_x:Number=0,init_y:Number=0,
    max_inputs:int=2):void{
        super(init_x,init_y,true,true,(max_inputs>=2?max_inputs:2),"NANDGate");
        FunctionToUse=UseAND;
    }
}

import flash.display.Sprite;
class NORGate extends LogicGate implements GUIInterface{
    public function ImplementMenuItems():Array{
        return SharedMenuItems;
    }
    public function NORGate(init_x:Number=0,init_y:Number=0,
    max_inputs:int=2):void{
        super(init_x,init_y,false,true,(max_inputs>=2?max_inputs:2),"NORGate");
        FunctionToUse=UseOR;
    }
}

import flash.display.Sprite;
class XNORGate extends LogicGate implements GUIInterface{
    public function ImplementMenuItems():Array{
        return SharedMenuItems;
    }
    public function XNORGate(init_x:Number=0,init_y:Number=0,
    max_inputs:int=2):void{
        super(init_x,init_y,false,true,(max_inputs>=2?max_inputs:2),"XNORGate");
        FunctionToUse=UseXOR;
    }
}