/***********************************************************************************************
 * Copyright (c) 2012  DCA-FEEC-UNICAMP
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v3
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 * <p>
 * Contributors:
 * K. Raizer, A. L. O. Paraense, E. M. Froes, R. R. Gudwin - initial API and implementation
 ***********************************************************************************************/
package br.unicamp.cst.attention;
import br.unicamp.cst.core.entities.MemoryObject;
import br.unicamp.cst.core.entities.Mind;
import br.unicamp.cst.support.TimeStamp;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Test for Codelet implementation of Top-Down Feature Maps generated by the 
 * Attentional System of Conscious Attention-Based Integrated Model (CONAIM).
 * 
 * @see Codelet
 * @see MemoryObject
 * @see FeatMapCodelet
  * @author L. L. Rossi (leolellisr)
 */
public class TopDownFeapMapCodeletTest {
    private static Logger log = LoggerFactory.getLogger(TopDownFeapMapCodeletTest.class);    
    public MemoryObject source;
    public MemoryObject destination;
    public TopDownFM testFeapMapCodelet, testFeapMapCodelet2;
    
    /**
     * Test class initialization for the Top-Down Feature Map. Creates a test 
     * mind, with 1 input and 1 output. The codelet to be tested is initialized as 
     * a TopDownFM and inserted into the created mind. 
     * Input and output are added to the codelet and it is set to 
     * publish-subscribe. The mind is then initiated.
     * 
     */
    
    public TopDownFeapMapCodeletTest() {
        Mind testMind = new Mind();
        source = testMind.createMemoryObject("SOURCE");
        destination = testMind.createMemoryObject("DESTINATION");
        destination.setI(new CopyOnWriteArrayList<Float>());
        CopyOnWriteArrayList<Float> goal = new CopyOnWriteArrayList<>();
        goal.add((float) 255);
        goal.add((float) 0);
        goal.add((float) 0);
        testFeapMapCodelet = new TopDownFM(0, "DESTINATION", 100, 16, goal, 255, 16, 4, 3, false);
        testMind.insertCodelet(testFeapMapCodelet);
        testFeapMapCodelet.addInput(source);
        testFeapMapCodelet.addOutput(destination);
        testFeapMapCodelet.setIsMemoryObserver(true);
	source.addMemoryObserver(testFeapMapCodelet);
        
        testFeapMapCodelet2 = new TopDownFM(0, "DESTINATION", 100, 16, goal, 255, 16, 4, 3, true);
        testMind.insertCodelet(testFeapMapCodelet2);
        testFeapMapCodelet2.addInput(source);
        testFeapMapCodelet2.addOutput(destination);
        testFeapMapCodelet2.setIsMemoryObserver(true);
	source.addMemoryObserver(testFeapMapCodelet2);
        
        testMind.start();        
    }
    
    /**
     * 
     * Input. Array with 768 elements representing a 16 x 16 resolution vision 
     * sensor with 256 pixels in 3 color channels. The pixel arrangement of the 
     * sensor is as follows:
     * [[R_1_1, G_1_1, B_1_1, R_1_2, ..., R_1_16, G_1_16, B_1_16],
     * [R_2_1, G_2_1, B_2_1, R_2_2, ..., R_2_16, G_2_16, B_2_16],
     * ...
     * [R_16_1, G_16_1, B_16_1, R_16_2, ..., R_16_16, G_16_16, B_16_16]]
     * 
     * Output. Array with 16 elements representing the vision sensor separated 
     * into 16 regions (4x4).
     * 
     * Test 1: Input have all elements equal to 255. With no saliences, the 
     * output array will only have elements equals 0.
     * 
     * Test 2: Input have first 192 elements equal to 100 (64 for each channel), 
     * then next 192 elements are equal to [0, 100, 100], 
     * then next 192 elements are equal to [100, 0, 100],
     * then next 192 elements are equal to [100, 100, 0].
     * 
     * Performing the average pool operation on each of the color channels, the 
     * following arrays are obtained:
     * Red channel:
     * [[100, 100, 100, 100],
     * [0, 0, 0, 0],
     * [100, 100, 100, 100],
     * [100, 100, 100, 100]]
     * 
     * Green channel:
     * [[100, 100, 100, 100],
     * [100, 100, 100, 100],
     * [0, 0, 0, 0],
     * [100, 100, 100, 100]]
     * 
     * Blue channel:
     * [[100, 100, 100, 100],
     * [100, 100, 100, 100],
     * [100, 100, 100, 100],
     * [0, 0, 0, 0]]
     * 
     * When comparing the values obtained with the operation with the desired 
     * goal, it is observed that the most distant values are in the red channel.
     * Using the comparison values, we get an output of:
     * [[0.25, 0.25, 0.25, 0.25],
     * [0, 0, 0, 0],
     * [0.25, 0.25, 0.25, 0.25],
     * [0.25, 0.25, 0.25, 0.25]]
     * 
     * Test 3: Input have first 192 elements equal to [125, 0, 0], 
     * then next 192 elements are equal to [0, 100, 0], 
     * then next 192 elements are equal to [0, 0, 200],
     * then next 192 elements are equal to [0, 0, 255].
     * 
     * Performing the average pool operation on each of the color channels, the 
     * following arrays are obtained:
     * Red channel:
     * [[125, 125, 125, 125],
     * [0, 0, 0, 0],
     * [0, 0, 0, 0],
     * [0, 0, 0, 0]]
     * 
     * Green channel:
     * [[0, 0, 0, 0],
     * [100, 100, 100, 100],
     * [0, 0, 0, 0],
     * [0, 0, 0, 0]]
     * 
     * Blue channel:
     * [[0, 0, 0, 0],
     * [0, 0, 0, 0],
     * [200, 200, 200, 200],
     * [255, 255, 255, 255]]
     * 
     * When comparing the values obtained with the operation with the desired 
     * goal, it is observed that the most distant values are in the red and blue channels.
     * Using the comparison values, we get an output of:
     * [[0.5, 0.5, 0.5, 0.5],
     * [0, 0, 0, 0],
     * [0, 0, 0, 0],
     * [0, 0, 0, 0]]
     * 
     * Test 4: Input have elements equal to [255, 0, 0].
     * 
     * Performing the average pool operation on each of the color channels, the 
     * following arrays are obtained:
     * Red channel:
     * [[255, 255, 255, 255],
     * [255, 255, 255, 255],
     * [255, 255, 255, 255],
     * [255, 255, 255, 255]]
     * 
     * Green channel:
     * [[0, 0, 0, 0],
     * [0, 0, 0, 0],
     * [0, 0, 0, 0],
     * [0, 0, 0, 0]]
     * 
     * Blue channel:
     * [[0, 0, 0, 0],
     * [0, 0, 0, 0],
     * [0, 0, 0, 0],
     * [0, 0, 0, 0]]
     * 
     * When comparing the values obtained with the operation with the desired 
     * goal, it is observed that all values are equal the goal.
     * Using the comparison values, we get an output of:
     * [[1, 1, 1, 1],
     * [1, 1, 1, 1],
     * [1, 1, 1, 1],
     * [1, 1, 1, 1]]
     */
    
    @Test
    public void testTopDownFeapMapCodelet() {
        TopDownFeapMapCodeletTest test = new TopDownFeapMapCodeletTest();
            System.out.println("Testing ... ");
            
            // Test 1
            CopyOnWriteArrayList<Float> arrList_test = new CopyOnWriteArrayList<Float>();
            for (int i = 0; i < 256*3; i++) {
                arrList_test.add((float) 255);
            }
            CopyOnWriteArrayList<Float> arrList_goal = new CopyOnWriteArrayList<Float>();
            
            for (int i = 0; i < 16; i++) {
                arrList_goal.add((float) 0);
            }
            
            long oldtimestamp = test.destination.getTimestamp();
            System.out.println("steps"+test.testFeapMapCodelet.steps+" Timestamp before: "+TimeStamp.getStringTimeStamp(oldtimestamp, "dd/MM/yyyy HH:mm:ss.SSS"));
            long newtimestamp = test.destination.getTimestamp();
            test.testFeapMapCodelet.resetTriggers();
            System.out.println("steps: "+test.testFeapMapCodelet.steps+" Timestamp before: "+TimeStamp.getStringTimeStamp(oldtimestamp, "dd/MM/yyyy HH:mm:ss.SSS"));
            test.source.setI(arrList_test);
            System.out.println("steps: "+test.testFeapMapCodelet.steps+" Timestamp before: "+TimeStamp.getStringTimeStamp(oldtimestamp, "dd/MM/yyyy HH:mm:ss.SSS"));
            
            while(test.testFeapMapCodelet.steps < 1) {
                newtimestamp = test.destination.getTimestamp();
                System.out.println("steps: "+test.testFeapMapCodelet.steps+" Timestamp during: "+TimeStamp.getStringTimeStamp(newtimestamp,"dd/MM/yyyy HH:mm:ss.SSS"));
            }
            System.out.println("steps: "+test.testFeapMapCodelet.steps);        
            System.out.println("   Input 1: "+test.source.getI()+"  \n size: "+((List)(test.source.getI())).size());
            System.out.println("   Output 1: "+ test.destination.getI());
            System.out.println("   Goal 1:"+arrList_goal);
            System.out.println("steps: "+test.testFeapMapCodelet.steps);        
            List fulllist = (List) test.destination.getI();
            System.out.println("steps: "+test.testFeapMapCodelet.steps);        
            if (fulllist != null && fulllist.size() > 0) {
                System.out.println("          sizef: "+((List)(fulllist)).size()+"\n");
                assertEquals(fulllist.size(),16);
                assertEquals(fulllist,arrList_goal);
            }
            System.out.println("steps: "+test.testFeapMapCodelet.steps);     
            
            // Test 2
            oldtimestamp = test.destination.getTimestamp();
            System.out.println("steps: "+test.testFeapMapCodelet.steps+" Timestamp before: "+TimeStamp.getStringTimeStamp(oldtimestamp, "dd/MM/yyyy HH:mm:ss.SSS"));
            
            arrList_test = new CopyOnWriteArrayList<Float>();
            for (int i = 0; i < (int)(256/4); i++) {
                arrList_test.add((float) 100);
                arrList_test.add((float) 100);
                arrList_test.add((float) 100);
            }
            for (int i = (int)(256/4); i < (int)(256/4*2); i++) {
                arrList_test.add((float) 0);
                arrList_test.add((float) 100);
                arrList_test.add((float) 100);
            }
            for (int i = (int)(256/4*2); i < (int)(256/4*3); i++) {
                arrList_test.add((float) 100);
                arrList_test.add((float) 0);
                arrList_test.add((float) 100);
            }
            for (int i = (int)(256/4*3); i < 256; i++) {
                arrList_test.add((float) 100);
                arrList_test.add((float) 100);
                arrList_test.add((float) 0);
            }
            arrList_goal = new CopyOnWriteArrayList<Float>();
            for (int i = 0; i < 4; i++) {
                arrList_goal.add((float) 0.25);
            }
            
            for (int i = 0; i < 4; i++) {
                arrList_goal.add((float) 0.0);
            }
            for (int i = 0; i < 8; i++) {
                arrList_goal.add((float) 0.25);
            }
            
            System.out.println("steps: "+test.testFeapMapCodelet.steps);        
            newtimestamp = test.destination.getTimestamp();
            test.testFeapMapCodelet.resetTriggers();
            System.out.println("steps: "+test.testFeapMapCodelet.steps);        
            test.source.setI(arrList_test);
            System.out.println("steps: "+test.testFeapMapCodelet.steps);        
            while(test.testFeapMapCodelet.steps < 1) {
                newtimestamp = test.destination.getTimestamp();
                System.out.println("steps: "+test.testFeapMapCodelet.steps+" Timestamp after: "+TimeStamp.getStringTimeStamp(newtimestamp,"dd/MM/yyyy HH:mm:ss.SSS"));
            }
            System.out.println("steps: "+test.testFeapMapCodelet.steps);        
            System.out.println("   Input 2: "+test.source.getI()+" \n  size: "+((List)(test.source.getI())).size());
            System.out.print("\n   Output 2: "+ test.destination.getI());
            System.out.println("\n   Goal 2:"+arrList_goal);
            fulllist = (List) test.destination.getI();
            if (fulllist != null && fulllist.size() > 0) {
                System.out.println("          sizef: "+((List)(test.destination.getI())).size()+"\n");
                assertEquals(fulllist.size(),16);
                assertEquals(fulllist,arrList_goal);
            }
            
            oldtimestamp = test.destination.getTimestamp();
            System.out.println("steps: "+test.testFeapMapCodelet.steps+" Timestamp before: "+TimeStamp.getStringTimeStamp(oldtimestamp, "dd/MM/yyyy HH:mm:ss.SSS"));
            
            // Test 3
            arrList_test = new CopyOnWriteArrayList<Float>();
            for (int i = 0; i < (int)(256/4); i++) {
                arrList_test.add((float) 125);
                arrList_test.add((float) 0);
                arrList_test.add((float) 0);
            }
            for (int i = (int)(256/4); i < (int)(256/4*2); i++) {
                arrList_test.add((float) 0);
                arrList_test.add((float) 100);
                arrList_test.add((float) 0);
            }
            for (int i = (int)(256/4*2); i < (int)(256/4*3); i++) {
                arrList_test.add((float) 0);
                arrList_test.add((float) 0);
                arrList_test.add((float) 200);
            }
            for (int i = (int)(256/4*3); i < 256; i++) {
                arrList_test.add((float) 0);
                arrList_test.add((float) 0);
                arrList_test.add((float) 255);
            }
            arrList_goal = new CopyOnWriteArrayList<Float>();
            for (int i = 0; i < 4; i++) {
                arrList_goal.add((float) 0.5);
            }
            for (int i = 0; i < 12; i++) {
                arrList_goal.add((float) 0.0);
            }
            
            newtimestamp = test.destination.getTimestamp();
            test.testFeapMapCodelet.resetTriggers();
            test.source.setI(arrList_test);
            
            while(test.testFeapMapCodelet.steps < 1) {
                newtimestamp = test.destination.getTimestamp();
                System.out.println("steps: "+test.testFeapMapCodelet.steps+" Timestamp after: "+TimeStamp.getStringTimeStamp(newtimestamp,"dd/MM/yyyy HH:mm:ss.SSS"));
            }
            System.out.println("   Input 3: "+test.source.getI()+" \n  size: "+((List)(test.source.getI())).size());
            System.out.println("\n   Output 3: "+ test.destination.getI());
            System.out.println("\n   Goal 3:"+arrList_goal);
            fulllist = (List) test.destination.getI();
            if (fulllist != null && fulllist.size() > 0) {
                System.out.println("          sizef: "+((List)(test.destination.getI())).size()+"\n");
                assertEquals(fulllist.size(),16);
                assertEquals(fulllist,arrList_goal);
            }
            
            // Test 4
            oldtimestamp = test.destination.getTimestamp();
            System.out.println("steps: "+test.testFeapMapCodelet.steps+" Timestamp before: "+TimeStamp.getStringTimeStamp(oldtimestamp, "dd/MM/yyyy HH:mm:ss.SSS"));
            
            arrList_test = new CopyOnWriteArrayList<Float>();
            for (int i = 0; i < (int)(256); i++) {
                arrList_test.add((float) 255);
                arrList_test.add((float) 0);
                arrList_test.add((float) 0);
            }
            
            arrList_goal = new CopyOnWriteArrayList<Float>();
            for (int i = 0; i < 16; i++) {
                arrList_goal.add((float) 1);
            }
            
            
            System.out.println("steps: "+test.testFeapMapCodelet.steps);        
            newtimestamp = test.destination.getTimestamp();
            test.testFeapMapCodelet.resetTriggers();
            System.out.println("steps: "+test.testFeapMapCodelet.steps);        
            test.source.setI(arrList_test);
            System.out.println("steps: "+test.testFeapMapCodelet.steps);        
            while(test.testFeapMapCodelet.steps < 1) {
                newtimestamp = test.destination.getTimestamp();
                System.out.println("steps: "+test.testFeapMapCodelet.steps+" Timestamp after: "+TimeStamp.getStringTimeStamp(newtimestamp,"dd/MM/yyyy HH:mm:ss.SSS"));
            }
            System.out.println("steps: "+test.testFeapMapCodelet.steps);        
            System.out.println("   Input 4: "+test.source.getI()+" \n  size: "+((List)(test.source.getI())).size());
            System.out.print("\n   Output 4: "+ test.destination.getI());
            System.out.println("\n   Goal 4:"+arrList_goal);
            fulllist = (List) test.destination.getI();
            if (fulllist != null && fulllist.size() > 0) {
                System.out.println("          sizef: "+((List)(test.destination.getI())).size()+"\n");
                assertEquals(fulllist.size(),16);
                assertEquals(fulllist,arrList_goal);
            }
    }
}
