/*
 * Copyright 2015 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

//created on: 25/03/2006
package org.benchmarks.waltz

import org.drools.benchmark.model.waltz.Stage
import org.drools.benchmark.model.waltz.Line
import org.drools.benchmark.model.waltz.Edge
import org.drools.benchmark.model.waltz.Junction
import org.drools.benchmark.model.waltz.WaltzUtil

global java.io.PrintStream sysout;

// the starting rule
rule "begin waltz"
    when
        $stage : Stage(value == Stage.START)
    then
        insert (new Line( 122,  107));
        insert (new Line( 107, 2207));
        insert (new Line(2207, 3204));
        insert (new Line(3204, 6404));
        insert (new Line(2216, 2207));
        insert (new Line(3213, 3204));
        insert (new Line(2216, 3213));
        insert (new Line( 107, 2601));
        insert (new Line(2601, 7401));
        insert (new Line(6404, 7401));
        insert (new Line(3213, 6413));
        insert (new Line(6413, 6404));
        insert (new Line(7416, 7401));
        insert (new Line(5216, 6413));
        insert (new Line(2216, 5216));
        insert (new Line( 122, 5222));
        insert (new Line(5222, 7416));
        insert (new Line(5222, 5216));
        $stage.setValue(Stage.DUPLICATE);
        update ( $stage );
        //sysout.println("Waltz started");
        //sysout.println("Stage: duplicate");
end

//If the duplicate flag is set, and there is still a line in WM, delete the line
//and add two edges. One edge runs from p1 to p2 and the other runs from p2 to
//p1.  We then plot the edge.
rule "reverse edges"
    when
        Stage(value == Stage.DUPLICATE)
        $line : Line ( $p1:p1, $p2:p2 )
    then
        //sysout.println("Draw "+$p1+" "+$p2);
        insert( new Edge ( $p1, $p2, false, Edge.NIL, false ) );
        insert( new Edge ( $p2, $p1, false, Edge.NIL, false ) );
        retract( $line );
end

//If the duplicating flag is set, and there are no more lines, then remove the
//duplicating flag and set the make junctions flag.
rule "reversing done" salience -10
    when
        $stage: Stage ( value == Stage.DUPLICATE )
        not ( Line() )
    then
        modify( $stage ) {
            setValue( Stage.DETECT_JUNCTIONS );
        }
        //sysout.println("reversing done");
end

//If three edges meet at a point and none of them have already been joined in
//a junction, then make the corresponding type of junction and label the
//edges joined.  This production calls make-3_junction to determine
//what type of junction it is based on the angles inscribed by the
//intersecting edges
rule "make 3 junction" salience 10
    when
        Stage ( value == Stage.DETECT_JUNCTIONS )
        $edge1: Edge( $basePoint:p1, $edge1P2:p2, joined==false )
        $edge2: Edge( p1==$basePoint, $edge2P2:p2 != $edge1P2, joined == false )
        $edge3: Edge( p1==$basePoint, $edge3P2:p2 != $edge1P2, p2 != $edge2P2, joined == false )
    then
        Junction junction = WaltzUtil.make_3_junction( $basePoint, $edge1P2, $edge2P2, $edge3P2 );
        insert( junction );
        $edge1.setJoined(true);
        $edge2.setJoined(true);
        $edge3.setJoined(true);
        update( $edge1 );
        update( $edge2 );
        update( $edge3 );
        //sysout.println("make 3 junction");
end

//If two, and only two, edges meet that have not already been joined, then
//the junction is an "L"
rule "make L"
    when
        Stage ( value == Stage.DETECT_JUNCTIONS )
        $edge1: Edge( $basePoint:p1, $edge1P2:p2, joined==false )
        $edge2: Edge( p1==$basePoint, $edge2P2:p2 != $edge1P2, joined == false )
        not Edge( p1==$basePoint, p2 != $edge1P2, p2 != $edge2P2 )
    then
        insert( new Junction($edge1P2, $edge2P2, 0, $basePoint, Junction.L) );
        $edge1.setJoined(true);
        $edge2.setJoined(true);
        update( $edge1 );
        update( $edge2 );
        //sysout.println("make L");
end

//If the detect junctions flag is set, and there are no more un_joined edges,
//set the find_initial_boundary flag
rule "detecting done" salience -10
    when
        $stage : Stage ( value == Stage.DETECT_JUNCTIONS )
     then
         $stage.setValue( Stage.FIND_INITIAL_BOUNDARY );
         update( $stage );
        //sysout.println("detecting done");
end

//If the initial boundary junction is an L, then we know it's labelling
rule "initial boundary junction L"
    when
        $stage : Stage ( value == Stage.FIND_INITIAL_BOUNDARY )
                 Junction( type == Junction.L, $basePoint:basePoint, $p1:p1, $p2:p2 )
        $edge1 : Edge ( p1 == $basePoint, p2 == $p1 )
        $edge2 : Edge ( p1 == $basePoint, p2 == $p2 )
        not Junction( basePoint > $basePoint )
    then
        $edge1.setLabel( Edge.B );
        $edge2.setLabel( Edge.B );
        $stage.setValue( Stage.FIND_SECOND_BOUNDARY );
        update( $edge1 );
        update( $edge2 );
        update( $stage );
        //sysout.println("\n\ninitial boundary junction L\n\n");
end

// Ditto for an arrow
rule "initial boundary junction arrow"
    when
        $stage : Stage ( value == Stage.FIND_INITIAL_BOUNDARY )
                 Junction( type == Junction.ARROW, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        $edge1 : Edge ( p1 == $basePoint, p2 == $p1 )
        $edge2 : Edge ( p1 == $basePoint, p2 == $p2 )
        $edge3 : Edge ( p1 == $basePoint, p2 == $p3 )
        not Junction( basePoint > $basePoint )
    then
        $edge1.setLabel( Edge.B );
        $edge2.setLabel( Edge.PLUS );
        $edge3.setLabel( Edge.B );
        $stage.setValue( Stage.FIND_SECOND_BOUNDARY );
        update( $edge1 );
        update( $edge2 );
        update( $edge3 );
        update( $stage );
        //sysout.println("initial boundary junction arrow");
end

// If we have already found the first boundary point, then find the second
// boundary point, and label it.
rule "second boundary junction L"
    when
        $stage : Stage ( value == Stage.FIND_SECOND_BOUNDARY )
                 Junction( type == Junction.L, $basePoint:basePoint, $p1:p1, $p2:p2 )
        $edge1 : Edge ( p1 == $basePoint, p2 == $p1 )
        $edge2 : Edge ( p1 == $basePoint, p2 == $p2 )
        not Junction( basePoint < $basePoint )
    then
        $edge1.setLabel( Edge.B );
        $edge2.setLabel( Edge.B );
        $stage.setValue( Stage.LABELING );
        update( $edge1 );
        update( $edge2 );
        update( $stage );
        //sysout.println("second boundary junction L");
end

// Ditto for arrow
rule "second boundary junction arrow"
    when
        $stage : Stage ( value == Stage.FIND_SECOND_BOUNDARY )
                 Junction( type == Junction.ARROW, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        $edge1 : Edge ( p1 == $basePoint, p2 == $p1 )
        $edge2 : Edge ( p1 == $basePoint, p2 == $p2 )
        $edge3 : Edge ( p1 == $basePoint, p2 == $p3 )
        not Junction( basePoint < $basePoint )
    then
        $edge1.setLabel( Edge.B );
        $edge2.setLabel( Edge.PLUS );
        $edge3.setLabel( Edge.B );
        $stage.setValue( Stage.LABELING );
        update( $edge1 );
        update( $edge2 );
        update( $edge3 );
        update( $stage );
        //sysout.println("\n\nsecond boundary junction arrow\n\n");
end
 
// If we have an edge whose label we already know definitely, then
// label the corresponding edge in the other direction
rule "match edge"
    when
        Stage( value == Stage.LABELING )
        $edge1: Edge( $p1:p1, $p2:p2, $label:label == Edge.PLUS || == Edge.MINUS || == Edge.B )
        $edge2: Edge( p1 == $p2, p2 == $p1, label == Edge.NIL )
    then
        $edge1.setPlotted( true );
        $edge2.setLabel( $label );
        $edge2.setPlotted( true );
        update( $edge1 );
        update( $edge2 );
        //sysout.println("match edge - Plot "+$label+" "+$p1+" "+$p2);
end

// The following productions propogate the possible labellings of the edges
// based on the labellings of edges incident on adjacent junctions.  Since
// from the initial boundary productions, we have determined the labellings of
// of atleast two junctions, this propogation will label all of the junctions
// with the possible labellings.  The search space is pruned due to filtering,
// i.e.(not only label a junction in the ways physically possible based on the
// labellings of adjacent junctions.
rule "label L"
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.L, $basePoint:basePoint )
        Edge( p1 == $basePoint, $Edge1P2 : p2, label == Edge.PLUS || == Edge.MINUS )
        $edge: Edge( p1 == $basePoint, p2 != $Edge1P2, label == Edge.NIL )
    then
        $edge.setLabel( Edge.B );
        update( $edge );
          //sysout.println("Label L");
end 
 
rule "label tee A" salience 5
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.TEE, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        $edge1: Edge( p1 == $basePoint, p2 == $p1, label == Edge.NIL )
        $edge2: Edge( p1 == $basePoint, p2 == $p3 )
    then
        $edge1.setLabel( Edge.B );
        $edge2.setLabel( Edge.B );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("Label tee A");
end 


rule "label tee B"
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.TEE, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        $edge1: Edge( p1 == $basePoint, p2 == $p1 )
        $edge2: Edge( p1 == $basePoint, p2 == $p3, label == Edge.NIL )
    then
        $edge1.setLabel( Edge.B );
        $edge2.setLabel( Edge.B );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("Label tee B");
end

rule "label fork 1"
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.FORK, $basePoint:basePoint )
                Edge( p1 == $basePoint, $p1:p2, label == Edge.PLUS )
        $edge1: Edge( p1 == $basePoint, $p2:p2 != $p1, label == Edge.NIL )
        $edge2: Edge( p1 == $basePoint, p2 != $p1, p2 != $p2 )
    then
        $edge1.setLabel( Edge.PLUS );
        $edge2.setLabel( Edge.PLUS );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("label fork 1");
end

rule "label fork 2"
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.FORK, $basePoint:basePoint )
        Edge( p1 == $basePoint, $p1:p2, label == Edge.B )
        Edge( p1 == $basePoint, $p2:p2 != $p1, label == Edge.MINUS )
        $edge: Edge( p1 == $basePoint, p2 != $p1, p2 != $p2, label == Edge.NIL )
    then
        $edge.setLabel( Edge.B );
        update( $edge );
          //sysout.println("label fork 2");
end

rule "label fork 3"
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.FORK, $basePoint:basePoint )
        Edge( p1 == $basePoint, $p1:p2, label == Edge.B )
        Edge( p1 == $basePoint, $p2:p2 != $p1, label == Edge.B )
        $edge: Edge( p1 == $basePoint, p2 != $p1, p2 != $p2, label == Edge.NIL )
    then
        $edge.setLabel( Edge.MINUS );
        update( $edge );
          //sysout.println("label fork 3");
end

rule "label fork 4"
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.FORK, $basePoint:basePoint )
        Edge( p1 == $basePoint, $p1:p2, label == Edge.MINUS )
        Edge( p1 == $basePoint, $p2:p2 != $p1, label == Edge.MINUS )
        $edge: Edge( p1 == $basePoint, p2 != $p1, p2 != $p2, label == Edge.NIL )
    then
        $edge.setLabel( Edge.MINUS );
        update( $edge );
          //sysout.println("label fork 4");
end
 
rule "label arrow 1A" salience 5
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.ARROW, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        Edge( p1 == $basePoint, p2 == $p1, $label:label == Edge.B || == Edge.MINUS )
        $edge1: Edge( p1 == $basePoint, p2 == $p2, label == Edge.NIL )
        $edge2: Edge( p1 == $basePoint, p2 == $p3 )
    then
        $edge1.setLabel( Edge.PLUS );
        $edge2.setLabel( $label );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("label arrow 1A");
end
 
rule "label arrow 1B" 
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.ARROW, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        Edge( p1 == $basePoint, p2 == $p1, $label:label == Edge.B || == Edge.MINUS )
        $edge1: Edge( p1 == $basePoint, p2 == $p2 )
        $edge2: Edge( p1 == $basePoint, p2 == $p3, label == Edge.NIL )
    then
        $edge1.setLabel( Edge.PLUS );
        $edge2.setLabel( $label );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("label arrow 1B");
end
 
rule "label arrow 2A" salience 5 
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.ARROW, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        Edge( p1 == $basePoint, p2 == $p3, $label:label == Edge.B || == Edge.MINUS )
        $edge1: Edge( p1 == $basePoint, p2 == $p2, label == Edge.NIL )
        $edge2: Edge( p1 == $basePoint, p2 == $p1 )
    then
        $edge1.setLabel( Edge.PLUS );
        $edge2.setLabel( $label );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("label arrow 2A");
end
 
rule "label arrow 2B"
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.ARROW, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        Edge( p1 == $basePoint, p2 == $p3,  $label:label == Edge.B || == Edge.MINUS  )
        $edge1: Edge( p1 == $basePoint, p2 == $p2 )
        $edge2: Edge( p1 == $basePoint, p2 == $p1, label == Edge.NIL )
    then
        $edge1.setLabel( Edge.PLUS );
        $edge2.setLabel( $label );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("label arrow 2B");
end
 
rule "label arrow 3A" salience 5 
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.ARROW, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        Edge( p1 == $basePoint, p2 == $p1, label == Edge.PLUS )
        $edge1: Edge( p1 == $basePoint, p2 == $p2, label == Edge.NIL )
        $edge2: Edge( p1 == $basePoint, p2 == $p3 )
    then
        $edge1.setLabel( Edge.MINUS );
        $edge2.setLabel( Edge.PLUS );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("label arrow 3A");
end
 
rule "label arrow 3B" 
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.ARROW, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        Edge( p1 == $basePoint, p2 == $p1, label == Edge.PLUS )
        $edge1: Edge( p1 == $basePoint, p2 == $p2 )
        $edge2: Edge( p1 == $basePoint, p2 == $p3, label == Edge.NIL )
    then
        $edge1.setLabel( Edge.MINUS );
        $edge2.setLabel( Edge.PLUS );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("label arrow 3B");
end
 
rule "label arrow 4A" salience 5 
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.ARROW, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        Edge( p1 == $basePoint, p2 == $p3, label == Edge.PLUS )
        $edge1: Edge( p1 == $basePoint, p2 == $p2, label == Edge.NIL )
        $edge2: Edge( p1 == $basePoint, p2 == $p1 )
    then
        $edge1.setLabel( Edge.MINUS );
        $edge2.setLabel( Edge.PLUS );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("label arrow 4A");
end
 
rule "label arrow 4B" 
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.ARROW, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        Edge( p1 == $basePoint, p2 == $p3, label == Edge.PLUS )
        $edge1: Edge( p1 == $basePoint, p2 == $p2 )
        $edge2: Edge( p1 == $basePoint, p2 == $p1, label == Edge.NIL )
    then
        $edge1.setLabel( Edge.MINUS );
        $edge2.setLabel( Edge.PLUS );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("label arrow 4B");
end
 
rule "label arrow 5A" salience 5 
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.ARROW, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        Edge( p1 == $basePoint, p2 == $p2, label == Edge.MINUS )
        $edge1: Edge( p1 == $basePoint, p2 == $p1 )
        $edge2: Edge( p1 == $basePoint, p2 == $p3, label == Edge.NIL )
    then
        $edge1.setLabel( Edge.PLUS );
        $edge2.setLabel( Edge.PLUS );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("label arrow 5A");
end

rule "label arrow 5B" 
    when
        Stage( value == Stage.LABELING )
        Junction( type == Junction.ARROW, $basePoint:basePoint, $p1:p1, $p2:p2, $p3:p3 )
        Edge( p1 == $basePoint, p2 == $p2, label == Edge.MINUS )
        $edge1: Edge( p1 == $basePoint, p2 == $p1, label == Edge.NIL )
        $edge2: Edge( p1 == $basePoint, p2 == $p3 )
    then
        $edge1.setLabel( Edge.PLUS );
        $edge2.setLabel( Edge.PLUS );
        update( $edge1 );
        update( $edge2 );
          //sysout.println("label arrow 5B");
end
 
// The conflict resolution mechanism will only execute a production if no
// productions that are more complicated are satisfied.  This production is
// simple, so all of the above dictionary productions will fire before this
// change of state production
rule "done labeling" salience -10
    when
        $stage: Stage( value == Stage.LABELING )
    then
        $stage.setValue( Stage.PLOT_REMAINING_EDGES );
        update( $stage );
        //sysout.println("done labeling");
end

// At this point, some labellings may have not been plotted, so plot them
rule "plot remaining"
    when
        Stage( value == Stage.PLOT_REMAINING_EDGES )
        $edge: Edge( plotted == false, $label:label != Edge.NIL, $p1:p1, $p2:p2 )
    then
        //sysout.println("Plot "+$label+" "+$p1+" "+$p2);
        $edge.setPlotted( true );
        update( $edge );
end

// If we have been un able to label an edge, assume that it is a boundary.
// This is a total Kludge, but what the hell. (if we assume only valid drawings
// will be given for labeling, this assumption generally is true!)
rule "plot boundaries"
    when
        Stage( value == Stage.PLOT_REMAINING_EDGES )
        $edge: Edge( plotted == false, label == Edge.NIL, $p1:p1, $p2:p2 )
    then
        //sysout.println("Plot B "+$p1+" "+$p2);
        $edge.setPlotted( true );
        update( $edge );
end

// If there is no more work to do, then we are done and flag it.
rule "done plotting" salience -10
    when
        $stage: Stage( value == Stage.PLOT_REMAINING_EDGES )
    then
        $stage.setValue( Stage.DONE );
        update( $stage );
        //sysout.println("Stage: done");
end
