package org.drools.agent;

import java.io.File;
import java.util.ArrayList;


import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.SystemEventListener;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class KnowledgeAgentDisposeTest extends BaseKnowledgeAgentTest {

    private int resourceChangeNotificationCount = 0;

    @Test
    public void testMonitorResourceChangeEvents() throws Exception {
        //create a basic dsl file
        this.fileManager.write("myExpander.dsl", this.createCommonDSL(null));

        //create a basic dslr file
        this.fileManager.write("rules.dslr", this.createCommonDSLRRule("Rule1"));

        String xml = "";
        xml += "<change-set xmlns='http://drools.org/drools-5.0/change-set'";
        xml += "    xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'";
        xml += "    xs:schemaLocation='http://drools.org/drools-5.0/change-set http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-api/src/main/resources/change-set-1.0.0.xsd' >";
        xml += "    <add> ";
        xml += "        <resource source='http://localhost:" + this.getPort() + "/rules.dslr' type='DSLR' />";
        xml += "        <resource source='http://localhost:" + this.getPort() + "/myExpander.dsl' type='DSL' />";
        xml += "    </add> ";
        xml += "</change-set>";

        File fxml = fileManager.write( "changeset.xml",
                                       xml );
        
        //Create a new Agent with newInstace=true
        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        KnowledgeAgent kagent = this.createKAgent(kbase, false);

        //Agent: take care of them!
        this.applyChangeSet(kagent, ResourceFactory.newUrlResource(fxml.toURI().toURL()));
        resourceChangeNotificationCount = 0;
        
        //the dsl is now modified.
        this.fileManager.write("myExpander.dsl", this.createCommonDSL("name == \"John\""));

        //the drl file is marked as modified too
        this.fileManager.write("rules.dslr", this.createCommonDSLRRule("Rule1"));

        this.scan(kagent);

        //two resources were modified, but only one change set is created
        assertEquals(1, resourceChangeNotificationCount);
        resourceChangeNotificationCount = 0;

        //let's add a new rule
        this.fileManager.write("rules.dslr", this.createCommonDSLRRule(new String[]{"Rule1","Rule2"}));

        this.scan(kagent);

        assertEquals(1, resourceChangeNotificationCount);
        resourceChangeNotificationCount = 0;

        //the kagent is stopped
        kagent.monitorResourceChangeEvents(false);

        //the dsrl file is marked as modified
        this.fileManager.write("rules.dslr", this.createCommonDSLRRule("Rule1"));

        try{
            this.scan(kagent);
            fail("The agent didn't process any change set. This should be failed.");
        }catch (RuntimeException e){
            assertEquals(e.getMessage(), "Event for KnowlegeBase update, due to scan, was never received");
        }
        assertEquals(0, resourceChangeNotificationCount);
        
        //let start the agent again
        kagent.monitorResourceChangeEvents(true);

        //the dsrl file is marked as modified
        this.fileManager.write("rules.dslr", this.createCommonDSLRRule("Rule1"));

        this.scan(kagent);

        assertEquals(1, resourceChangeNotificationCount);

        kagent.monitorResourceChangeEvents(false);
    }


    @Test
    public void testDispose() throws Exception {

        //create a basic dsl file
        this.fileManager.write("myExpander.dsl", this.createCommonDSL(null));

        //create a basic dslr file
        this.fileManager.write("rules.dslr", this.createCommonDSLRRule("Rule1"));

        String xml = "";
        xml += "<change-set xmlns='http://drools.org/drools-5.0/change-set'";
        xml += "    xmlns:xs='http://www.w3.org/2001/XMLSchema-instance'";
        xml += "    xs:schemaLocation='http://drools.org/drools-5.0/change-set http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-api/src/main/resources/change-set-1.0.0.xsd' >";
        xml += "    <add> ";
        xml += "        <resource source='http://localhost:" + this.getPort() + "/rules.dslr' type='DSLR' />";
        xml += "        <resource source='http://localhost:" + this.getPort() + "/myExpander.dsl' type='DSL' />";
        xml += "    </add> ";
        xml += "</change-set>";
        File fxml = fileManager.write( "changeset.xml",
                                       xml );

        //Create a new Agent with newInstace=false
        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        KnowledgeAgent kagent = this.createKAgent( kbase, false );

        //Agent: take care of them!
        this.applyChangeSet(kagent,ResourceFactory.newUrlResource(fxml.toURI().toURL()));

        resourceChangeNotificationCount = 0;

        //the dsl is now modified.
        this.fileManager.write("myExpander.dsl", this.createCommonDSL("name == \"John\""));

        //We also need to mark the dslr file as modified, so the rules could
        //be regenerated
        this.fileManager.write("rules.dslr", this.createCommonDSLRRule("Rule1"));

        this.scan(kagent);
        
        //two resources were modified, but only one change set is created
        assertEquals(1, resourceChangeNotificationCount);
        resourceChangeNotificationCount = 0;

        //let us create a new ksession and fire all the rules
        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        ksession.setGlobal("list", new ArrayList<String>());
        ksession.fireAllRules();


        //let's add a new rule
        this.fileManager.write("rules.dslr", this.createCommonDSLRRule(new String[]{"Rule1","Rule2"}));

        this.scan(kagent);

        assertEquals(1, resourceChangeNotificationCount);
        resourceChangeNotificationCount = 0;

        //the old ksession can be reused
        ksession.fireAllRules();


        //We will try to dispose the kagent: but an active stateful ksession exists.
        try{
            kagent.dispose();
            fail("The agent should failed");
        } catch (IllegalStateException ex){
        }

        //We need to dispose the ksession first
        ksession.dispose();

        //Now it is safe to dispose the kagent
        kagent.dispose();

        //the dsrl file is modified
        this.fileManager.write("rules.dslr", this.createCommonDSLRRule("Rule1"));

        try{
            this.scan(kagent);
            fail("The agent didn't process any change set. This should be failed.");
        }catch (RuntimeException e){
            assertEquals(e.getMessage(), "Event for KnowlegeBase update, due to scan, was never received");
        }
        assertEquals(0, resourceChangeNotificationCount);


    }


    @Override
    public KnowledgeAgent createKAgent(KnowledgeBase kbase, boolean newInstance) {
        KnowledgeAgent kagent = super.createKAgent(kbase, newInstance);

        kagent.setSystemEventListener(new SystemEventListener() {

            public void info(String message) {
            }

            public void info(String message, Object object) {
            }

            public void warning(String message) {
            }

            public void warning(String message, Object object) {
            }

            public void exception(String message, Throwable e) {
            }

            public void exception(Throwable e) {
            }

            public void debug(String message) {
                if ("KnowledgeAgent received ChangeSet changed notification".equals(message)){
                    resourceChangeNotificationCount++;
                }
            }

            public void debug(String message, Object object) {
            }
        });

        return kagent;
    }
}
