/*
 * /*******************************************************************************
 *  * 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
 *  * 
 *  * Contributors:
 *  *     K. Raizer, A. L. O. Paraense, R. R. Gudwin - initial API and implementation
 *  ******************************************************************************/
 
package br.unicamp.cst.attention;

import br.unicamp.cst.core.entities.Codelet;
import br.unicamp.cst.core.entities.MemoryObject;
import br.unicamp.cst.support.TimeStamp;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import br.unicamp.cst.support.ToTxt;

/**
 * Codelet implementation of a Combined Feature Map (CFM). The CFM is a signal 
 * composed of all feature maps generated (bottom-up and top-down), obtained 
 * through the sum of the products between an element of the feature weight 
 * vector in the attentional system (w_{f_{j_t}}) and a given feature map (F_{i_{j_t}}). 
 * 
 * The type of the winning characteristic of the bottom-up or top-down cycle is 
 * classified before the application of the feedback inhibition algorithm. 
 * 
 * Two values​are obtained during the formation of the Combined Feature Map: 
 * (i) the sum of the values​generated by the bottom-up feature maps and 
 * (ii) the sum of the values ​​generated by the top-down feature maps. 
 * 
 * If (i) > (ii), the winner of the attentional cycle is classified as bottom-up. 
 * If (ii) > (i), the winner of the attentional cycle is classified as top-down.
 * 
 * @author leolellisr
 * 
 * @see Codelet
 * @see MemoryObject
 * @see FeatMapCodelet
 * @see CombFeatMapCodelet
 */
public class CFM extends CombFeatMapCodelet {
    private static final int BOTTOM_UP = 0;
    private static final int TOP_DOWN = 1;
    private  int time_graph;
    public int steps;
    private boolean print_to_file = false, debug;
    private List<Float> CFMrow;
    private List<Integer>  winners_row;
    
    /**
     * init CFM class
     * @param featmapsnames
     *          input feature maps names
     * @param timeWin
     *          analysed time window,   Buffer size
     * @param CFMdim 
     *          output combined feature map dimension
     * @param print_to_file
     *          boolean that defines if should print to file
     * @param debug
     *          boolean that defines if should print debug logs
     */
    public CFM(CopyOnWriteArrayList<String> featmapsnames, int timeWin, int CFMdim, boolean print_to_file, boolean debug) {
        super(featmapsnames,timeWin,CFMdim);
        this.time_graph = 0;
        this.print_to_file = print_to_file;
        this.steps = 0;
        this.debug = debug;
    }

     /**
     * init CFMrow and WinnersRow. Initializes lists to receive new values of
     * CFM and Winners.
     */
    private void initializeCFMrowAndWinnersRow() {
    for (int j = 0; j < CFMdimension; j++) {
        this.CFMrow.add((float) 0);
        this.winners_row.add(0);
        }
    }
    
    /**
     * Calculate CFM and winners type. Calculates the CFM through the sum of the 
     * products between an element of the feature weight vector in the 
     * attentional system and a given feature map, and defines the type of the 
     * winning characteristic (bottom-up or top-down)
     */
    private void calculateCFMandWinners() {
        List<Float> weight_values = (List<Float>) weights.getI();
        for (int j = 0; j < CFMrow.size(); j++) { 
                float ctj= 0, sum_top=0, sum_bottom=0;
                for (int k = 0; k < feat_maps_names.size(); k++) { 
                    MemoryObject FMkMO = (MemoryObject) feature_maps.get(k);
                    List<List<Float>> FMk = (List<List<Float>>) FMkMO.getI();
                    if(FMk == null || weight_values == null) return;
                    if(FMk.size() < 1) return;
                    List<Float> FMk_t = (List<Float>) FMk.get(FMk.size()-1);
                    Float fmkt_val = FMk_t.get(j), weight_val = weight_values.get(k);
                    ctj += weight_val*fmkt_val;
                    if(k>=4) sum_top += weight_val*fmkt_val;
                    else sum_bottom += weight_val*fmkt_val; 
                }   
                CFMrow.set(j, ctj);
                if(sum_top > sum_bottom) winners_row.set(j, TOP_DOWN);
                else winners_row.set(j, BOTTOM_UP); 
            }
    }
    
    /**
     * Reset triggers.
     */
    public void resetTriggers() {
        steps = 0;
    }
    
    /**
     * Main class of CFM. Executed every proc. Initializes the new arrays for 
     * CFM and winners
     */
    @Override
    public void calculateCombFeatMap() {
        try { Thread.sleep(300); } catch (Exception e) { Thread.currentThread().interrupt();}
        List<List<Float>> combinedFM = (List<List<Float>>) comb_feature_mapMO.getI();
        List<List<Integer>> winnersTypeList = (List<List<Integer>>) winnersType.getI();
        if(combinedFM.size() == timeWindow) combinedFM.remove(0);
        if(winnersTypeList.size() == timeWindow) winnersTypeList.remove(0);
        combinedFM.add(new CopyOnWriteArrayList<>());
        winnersTypeList.add(new CopyOnWriteArrayList<>());
        CFMrow = (List<Float>) combinedFM.get(combinedFM.size()-1);
        winners_row = (List<Integer>) winnersTypeList.get(winnersTypeList.size()-1);
        initializeCFMrowAndWinnersRow();        
        calculateCFMandWinners();
        if(debug){ Logger.getAnonymousLogger().log(Level.INFO, "I received {0} maps as inputs", feat_maps_names.size());
            for (int i = 0; i < feat_maps_names.size(); i++) {
                MemoryObject inp = (MemoryObject)feature_maps.get(i);
                Logger.getAnonymousLogger().log(Level.INFO, "{0} {1} : steps: {2} Timestamp after: {3}",  new Object[]{i, feat_maps_names.get(i), this.steps, TimeStamp.getStringTimeStamp(inp.getTimestamp(),"dd/MM/yyyy HH:mm:ss.SSS")});
            }
        }
        comb_feature_mapMO.setI(CFMrow);
        if(debug) Logger.getAnonymousLogger().log(Level.INFO, "CFM: steps: {0} Timestamp after: {1}",  new Object[]{this.steps, TimeStamp.getStringTimeStamp(comb_feature_mapMO.getTimestamp(),"dd/MM/yyyy HH:mm:ss.SSS")});
        if(print_to_file){ ToTxt.printToFile((CopyOnWriteArrayList<Float>) CFMrow, "tests/CFM.txt", debug, time_graph);
            ToTxt.printToFile((CopyOnWriteArrayList<Integer>) winners_row, "tests/winnerType.txt", debug, time_graph);
            time_graph++;
        }
        steps++;
    }
    
}