function DebugOutput(data, msg)
{
    if(msg)
    {
        console.log(msg);
    }
    console.log(data);
}

var VoidSegment = new Class
({
    /* Class Attributes */
    X1: false,
    X2: false,
    isHatted: false,
    
    /* Constructor */
    initialize: function(X1, X2)
    {
        this.X1 = X1;
        this.X2 = X2;
    },
    GetSize: function() {
        return (this.X2 - this.X1) + 1;
    },
    SetHatted: function(){
        this.isHatted = true;
    },
    IsHatted: function(){
        return this.isHatted;
    }
});

var Row = new Class
({
    /* Class Attributes */
    NumVoidSegments: 0,
    Segments: [],
    LinkedVoidMap: [],
    asString: '',
    
    /* Constructor */
    initialize: function(rowString)
    {
        //- Make sure the rowString is ONLY 21 characters long.
        //- If it is more, then trim down to 21, if it is less
        //- then pad with spaces
        //DebugOutput('::' + rowString + ':: ('+rowString.length+')');
        if(rowString.length > 21)
        {
            rowString = rowString.substring(0, 21);
        }
        else if(rowString.length < 21)
        {
            rowString = rowString.pad('21', ' ', 'right');
        }
        this.asString = rowString;
        //DebugOutput('::' + rowString + ':: ('+rowString.length+')');
        
        //-  Add on the void segments at the start and end
        rowString = "#"+rowString+"#";
        
        //- Take the row string we have been passed in and break it into void segments
        voidSegment = false;
        for(i=-1; i<22; i++)
        {
            rowIndex = i + 1;
            //- First we need to check if we have a char at this position. If not assume that it's a space
            if(typeof rowString[rowIndex] == 'undefined')
            {
                tile = ' ';
            }
            else
            {
                tile = rowString[rowIndex];
            }
            
            //  If we have found a "flip flop", i.e. it's gone from void to space
            if(voidSegment && !this.voidTile(tile)) {
                //- We've now moved into a non-void segment, log the void segment we have just moved out of into the segments
                //- array, it's end position being the current position -1
                voidSegment = false;
                this.Segments.push(new VoidSegment(voidStart, i-1));
            }
            else if ( ! voidSegment && this.voidTile(tile) )
            {
                //- We've now moved into a void segment, log the start position, increase the void segment count
                this.NumVoidSegments++;
                voidSegment = true;
                voidStart = i;
            }
        }
        
        //- Check if we've finished the loop inside a void segment, if so add this segment to the list
        if(voidSegment)
        {
            this.Segments.push(new VoidSegment(voidStart, 21));
        }
        
        //DebugOutput('::' + rowString + '::');
        //DebugOutput(this.Segments);
    },
    voidTile: function(tile)
    {
        return (tile == '/' || tile == '\\' || tile == '#');
        
    }
});

var MergedSegments = new Class
({
    /* Class Attributes */
    linkedSegments: [],
    topRow: false,
    bottomRow: false,
    minimumMergeLength: 0,
    output: [],
    
    /* Constructor */
    initialize: function(topRowString, bottomRowString, minLength)
    {
        this.topRow    = new Row(topRowString);
        this.bottomRow = new Row(bottomRowString);
        
        if(minLength > 0)
        {
            this.minimumMergeLength = minLength;
        }
        
        //-
        //- See if we need to HAT any rows
        //-
        if( this.topRow.NumVoidSegments != this.bottomRow.NumVoidSegments)
        {
            //- Work out how many need to be HATted
            NumToHat = Math.abs(this.topRow.NumVoidSegments - this.bottomRow.NumVoidSegments);
        
            //- Which Row has the most!
            RowToHat = this.topRow.NumVoidSegments > this.bottomRow.NumVoidSegments ? this.topRow : this.bottomRow;
            
            //- Set 'NumToHat' void segments as 'IsHatted'
            for(hat=0;hat<NumToHat;hat++)
            {
                smallestSize = 9999;
                candidates = [];
                
                //- Create a list of 'shortest' pieces
                for( candidate=1; candidate<RowToHat.NumVoidSegments-1; candidate++ )
                {
                    segment = RowToHat.Segments[candidate];
                    if( !segment.IsHatted() )
                    {
                        if( segment.GetSize() <= smallestSize )
                        {
                            candidates.push(segment);
                            smallestSize = segment.GetSize();
                        }
                    }
                }
                
                //- HAT a random 'shortest' void section (until we've HATed enough!)
                if( candidates.length > 0 )
                {
                    //- We found some candidates
                    picked = candidates.getRandom();
                    picked.SetHatted();
                }
            }
        }
        
        //-
        //- Build the Link Map table
        //-
        //DebugOutput( numTopSegs, "NUM VOID TOP" )
        for( upper=0; upper<this.topRow.NumVoidSegments; upper++)
        {
            segment = this.topRow.Segments[upper];
            if( ! segment.IsHatted() )
            {
                this.topRow.LinkedVoidMap.push(segment);
            }
        }
        for( bottom=0; bottom<this.bottomRow.NumVoidSegments; bottom++)
        {
            segment = this.bottomRow.Segments[bottom];
            if( ! segment.IsHatted() )
            {
                this.bottomRow.LinkedVoidMap.push(segment);
            }
        }
        
    
        //DebugOutput(this.topRow.LinkedVoidMap, 'TOP MAP');
        //DebugOutput(this.bottomRow.LinkedVoidMap, 'BOTTOM MAP');
        
        //-
        //- Make sure we have the 'minimum length' correctly calculated (non-HATted only)
        //-
        for(map=0;map<this.topRow.LinkedVoidMap.length;map++)
        {
            topSegment    = this.topRow.LinkedVoidMap[map];
            bottomSegment = this.bottomRow.LinkedVoidMap[map];
        
            dX1 = Math.abs(topSegment.X1 - bottomSegment.X1);
            dX2 = Math.abs(topSegment.X2 - bottomSegment.X2);
            maxSize = dX1 > dX2 ? dX1 : dX2;
            
            if( maxSize > this.minimumMergeLength ) this.minimumMergeLength = maxSize;
        }
            
        //-
        //- Make sure we have the 'minimum length' correctly calculated (HATted only)
        //-
        for(i=0; i<this.topRow.Segments.length; i++)
        {
            //- If we are a HATted piece work out the minimum height by getting half the height
            //- NOTE: This will need to change if we do random HAT type
            segment = this.topRow.Segments[i];
            if( segment.IsHatted() )
            {
                maxSize = parseInt( segment.GetSize() / 2 );
            }
            
            if( maxSize > this.minimumMergeLength ) this.minimumMergeLength = maxSize;
        }
        for(i=0; i<this.bottomRow.Segments.length; i++)
        {
            //- If we are a HATted piece work out the minimum height by getting half the height
            //- NOTE: This will need to change if we do random HAT type
            segment = this.bottomRow.Segments[i];
            if( segment.IsHatted() )
            {
                maxSize = parseInt( segment.GetSize() / 2 );
            }
            
            if( maxSize > this.minimumMergeLength ) this.minimumMergeLength = maxSize;
        }
        
        //- Now we have everything we need! (HOORAY!)
        //DebugOutput(this.minimumMergeLength, 'Min merge length');
        
        //- We need to create an array 21 wide and this.minimumMergeLength deep to hold our merged data
        for(row=0; row<this.minimumMergeLength; row++)
        {
            this.output[row] = new Array();                
            for(col=0; col<21; col++) this.output[row][col] = ' ';
        }

        this.doLinkLines();
        this.doHatted();
        
        //DebugOutput(topRowString);
        //DebugOutput(this.outputString());
        //DebugOutput(bottomRowString);
    },
    
    
    outputString: function(){
        output = '';
        
        //- Loop through the output array and create a string
        for(i=0; i<this.output.length; i++)
        {
            for(x=0; x<this.output[i].length; x++)
            {
                output += this.output[i][x];
            }
            output += "\n";
        }
        
        return output;
    },
    
    doLinkLines: function(){
    
        numSegments = this.topRow.LinkedVoidMap.length;
        
        for(map=0; map<this.topRow.LinkedVoidMap.length; map++)
        {
            topSegment = this.topRow.LinkedVoidMap[map];
            bottomSegment = this.bottomRow.LinkedVoidMap[map];
            
            //- Draw 'left' segment line (only if it's not the far right double void case!)
            if( topSegment.X1 < 21 || bottomSegment.X1 < 21 )
            {
                this.drawLine(topSegment.X1, bottomSegment.X1, this.minimumMergeLength, true);
            }
            //- Draw 'right;' segment (only if it's not the far left double void case!)
            if( topSegment.X2 > -1 || bottomSegment.X2 > -1 )
            {
                this.drawLine(topSegment.X2, bottomSegment.X2, this.minimumMergeLength, false);
            }
            
            this.floodFill( topSegment, bottomSegment );
            this.floodFillLine( topSegment, bottomSegment );
        
        }
    },
    
    doHatted: function(){
        for(seg=0; seg<this.topRow.Segments.length; seg++)
        {
            segment = this.topRow.Segments[seg];
            //- Check is this segment is "HATted"
            if( segment.IsHatted() )
            {
                this.drawHat(segment.X1, segment.X2, true);
            }
        }
        
        for(seg=0; seg<this.bottomRow.Segments.length; seg++)
        {
            segment = this.bottomRow.Segments[seg];
            //- Check is this segment is "HATted"
            if( segment.IsHatted() )
            {
                this.drawHat(segment.X1, segment.X2, false);
            }
        }
    },
    
    floodFill: function(topSegment, bottomSegment){
        topLength    = topSegment.X2 - topSegment.X1;
        bottomLength = bottomSegment.X2 - bottomSegment.X1;
        
        maxLength = topLength > bottomLength ? topLength : bottomLength;
        
        if( maxLength > 0 )
        {
            if( topLength == maxLength )
            {
                x = topSegment.X1;
                y = 0;
            }
            else
            {
                x = bottomSegment.X1;
                y = this.minimumMergeLength-1;
            }
            //DebugOutput('Flood Fill: ('+x+','+y+')');
            //DebugOutput('Flood Fill: ('+(x+1)+','+y+')');
             this.floodFillCell( x,   y);    
            this.floodFillCell( x+1, y);    
        }
    },
    
    floodFillLine: function(topSegment, bottomSegment){
        topLength    = topSegment.X2 - topSegment.X1;
        bottomLength = bottomSegment.X2 - bottomSegment.X1;
        
        maxLength = topLength > bottomLength ? topLength : bottomLength;
        //DebugOutput('Flood Fill: ML: '+maxLength);
        
        if( maxLength > 0 )
        {
            if( topLength == maxLength )
            {
                x = topSegment.X1;
                y = 0;
            }
            else
            {
                x = bottomSegment.X1;
                y = this.minimumMergeLength-1;
            }
            
            //- Draw 'left' segment line (only if it's not the far right double void case!)
            if( topSegment.X1 < 21 || bottomSegment.X1 < 21 )
            {
                this.drawLineFlood(topSegment.X1, bottomSegment.X1, this.minimumMergeLength, true);
            }
            //- Draw 'right;' segment (only if it's not the far left double void case!)
            if( topSegment.X2 > -1 || bottomSegment.X2 > -1 )
            {
                this.drawLineFlood(topSegment.X2, bottomSegment.X2, this.minimumMergeLength, false);
            }
        }
    },
    
    floodFillCell: function(x, y)
    {
        if (x<0 || x>=21) return;
        if (y<0 || y>=this.minimumMergeLength) return;
        
         existing = this.output[y][x];
         
         if (existing != '/' && existing != '\\' && existing != '#')
         {
             this.output[y][x] = '#';
             this.floodFillCell(x-1, y);
             this.floodFillCell(x+1, y);
             this.floodFillCell(x, y-1);
             this.floodFillCell(x, y+1);
         }
    
    },
    
    drawHat: function(x1, x2, isTop){
        //- Get the "height" of the HAT
        capWidth = (x2 - x1) +1;
        height = parseInt(capWidth / 2);
        
        //- If we are only 1 wide
        if(capWidth==1)
        {
            if(isTop)
            {
                //- Cap with random / or \
                this.output[0][x1] = ['\\', '/'].getRandom();
            }
            else
            {
                //- Cap with random / or \
                this.output[this.minimumMergeLength-1][x1] = ['\\', '/'].getRandom();
            }
        }
        else
        {
            if(isTop)
            {
                for(row=0; row<height; row++)
                {
                    this.output[row][x1] = '\\';
                    this.output[row][x2] = '/';
                    
                    //- Increase X1 and decrease X2
                    x1++;
                    x2--;
                }
                
                //- If this cap is > 2 wide and is an odd number
                capMid = x1 + (x2 - x1)
                if(capWidth > 2 && capWidth % 2 != 0)
                {
                    this.output[row-1][capMid] = '#';
                }
                
                if(height > 1)
                {
                    this.floodFillCell(capMid, 0);
                }
            }
            else
            {
                for(row=this.minimumMergeLength-1; row>(this.minimumMergeLength-height)-1; row--)
                {
                    this.output[row][x1] = '/';
                    this.output[row][x2] = '\\';
                    
                    //- Increase X1 and decrease X2
                    x1++;
                    x2--;
                }
                
                //- If this cap is > 2 wide and is an odd number
                capMid = x1 + (x2 - x1)
                if(capWidth > 2 && capWidth % 2 != 0)
                {
                    this.output[row+1][capMid] = '#';
                }
                
                if(height > 1)
                {
                    this.floodFillCell(capMid, this.minimumMergeLength-1);
                }
            }
        }
    },
    
    drawLine: function(x1, x2, h, isLeft){
        w = Math.abs(x2-x1);
        
        if( w == 0 )
        {
            //- Vertical line case!  (use #'s!)
            if( x1 > -1 && x1 < 21 )
            {
                for( row=0; row<h; row++ ) this.output[row][x1] = "#";
            }
        }
        else
        {
            //- Diagonal line case!  (use slashes!)
            //DebugOutput('Drawing DIAGONAL line from top: '+x1+' to bottom '+x2+' of height '+h);
            
            //- assumption: h >= w
            dX = w/h;    //- ALWAYS LESS THAN OR EQUAL 1!
            xSoFar = 0.0;
    
            col = x1;
            if( isLeft )
            {
                 if (x1 > x2 ) col--;
            }
            else
            {
                 if (x1 < x2 ) col++;
            }
            
            for( row=0; row<h; row++ )
            {
                //- Make sure we're not in the "outside" void zone
                if(col > -1 && col < 21)
                {
                    //DebugOutput('Drawing SHIFTED line on: Row '+row+' Col: '+col);
                    this.output[row][col] = (x1 < x2) ? "\\" : "/";
                }
                
                xSoFar += dX;
                if( xSoFar >= 1.0 )
                {
                    col = col + (x1 < x2 ? 1 : -1);        //- step us one in the appropriate direction!
                    xSoFar -= 1.0;
                }
            }
        
        }
    },
    
    drawLineFlood: function(x1, x2, h, isLeft){
        w = Math.abs(x2-x1);
        
        if( w > 0 )
        {
            //- Diagonal line case!  (use slashes!)
            //- assumption: h >= w
            dX = w/h;    //- ALWAYS LESS THAN OR EQUAL 1!
            xSoFar = 0.0;
    
            col = x1;
            if( isLeft )
            {
                 if (x1 > x2 ) col--;
            }
            else
            {
                 if (x1 < x2 ) col++;
            }
            
            for( row=0; row<h; row++ )
            {
                //- Make sure we're not in the "outside" void zone
                if(col > -1 && col < 21)
                {
                    if(isLeft)
                    {
                        this.floodFillCell(col+1, row);
                    }
                    else
                    {
                        this.floodFillCell(col-1, row);
                    }
                }
                
                xSoFar += dX;
                if( xSoFar >= 1.0 )
                {
                    col = col + (x1 < x2 ? 1 : -1);        //- step us one in the appropriate direction!
                    xSoFar -= 1.0;
                }
            }
        
        }
    }
});




