/* Copyright (c) 2011 Danish Maritime Authority
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */

package dk.dma.ais.abnormal.stat.statistics;

import dk.dma.ais.abnormal.stat.AppStatisticsService;
import dk.dma.ais.abnormal.stat.db.StatisticDataRepository;
import dk.dma.ais.abnormal.stat.db.data.CourseOverGroundStatisticData;
import dk.dma.ais.abnormal.stat.db.data.StatisticData;
import dk.dma.ais.abnormal.util.Categorizer;
import dk.dma.ais.packet.AisPacket;
import dk.dma.ais.test.helpers.ArgumentCaptor;
import dk.dma.ais.tracker.eventEmittingTracker.EventEmittingTracker;
import dk.dma.ais.tracker.eventEmittingTracker.Track;
import dk.dma.ais.tracker.eventEmittingTracker.events.CellChangedEvent;
import org.jmock.Expectations;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Before;
import org.junit.Test;

import java.util.HashMap;
import java.util.TreeMap;

import static org.junit.Assert.assertEquals;

public class CourseOverGroundStatisticTest {
    final JUnit4Mockery context = new JUnit4Mockery();

    EventEmittingTracker trackingService;
    AppStatisticsService statisticsService;
    StatisticDataRepository statisticsRepository;

    Track track;
    CellChangedEvent event;

    CourseOverGroundStatistic statistic;

    final String[] NMEA_TEST_STRINGS = {
        // GatehouseSourceTag [baseMmsi=2190067, country=DK, region=, timestamp=Thu Apr 10 15:30:28 CEST 2014]
        // [msgId=1, repeat=0, userId=219000606, cog=2010, navStatus=0, pos=(33024811,6011092) = (33024811,6011092), posAcc=1, raim=0, specialManIndicator=0, rot=0, sog=108, spare=0, syncState=1, trueHeading=200, utcSec=60, slotTimeout=6, subMessage=1063]
        "$PGHP,1,2014,4,10,13,30,28,385,219,,2190067,1,12*26\r\n" +
        "!BSVDM,1,1,,A,[email protected]`OOc:[email protected],0*12",

        // GatehouseSourceTag [baseMmsi=2190067, country=DK, region=, timestamp=Thu Apr 10 15:30:29 CEST 2014]
        // [msgId=5, repeat=0, userId=219000606, [email protected]@@, [email protected]@@@@, dimBow=12, dimPort=8, dimStarboard=4, dimStern=58, draught=30, dte=0, eta=67584, imo=8222824, name=FRIGG [email protected]@@@@@@, posType=1, shipType=61, spare=0, version=0]
        "$PGHP,1,2014,4,10,13,30,29,165,219,,2190067,1,28*22\r\n" +
        "!BSVDM,2,1,1,A,[email protected][email protected]@P07PSiBQ1,0*7B\r\n" +
        "!BSVDM,2,2,1,A,CcAVCTj0EP00000,2*53"
    };

    final AisPacket[] packets = { AisPacket.from(NMEA_TEST_STRINGS[0]), AisPacket.from(NMEA_TEST_STRINGS[1])};

    @Before
    public void beforeTest() {
        // Mock dependencies
        trackingService = context.mock(EventEmittingTracker.class);
        statisticsService = context.mock(AppStatisticsService.class);
        statisticsRepository = context.mock(StatisticDataRepository.class);

        // Setup test data
        track = new Track(219000606);
        track.setProperty(Track.CELL_ID, 5674365784L);
        track.update(packets[0]);
        track.update(packets[1]);

        event = new CellChangedEvent(track, null);

        statistic = new CourseOverGroundStatistic(statisticsService, trackingService, statisticsRepository);

        assertEquals(3, Categorizer.mapShipTypeToCategory(track.getShipType()));
        assertEquals(7, Categorizer.mapCourseOverGroundToCategory(track.getCourseOverGround()));
    }

    @Test
    public void testNewShipCountIsCreated() {
        // Setup expectations
        final ArgumentCaptor<StatisticData> statistics = ArgumentCaptor.forClass(StatisticData.class);
        context.checking(new Expectations() {{
            oneOf(trackingService).registerSubscriber(statistic);
            ignoring(statisticsService).incStatisticStatistics(with(CourseOverGroundStatistic.STATISTIC_NAME), with(any(String.class)));

            oneOf(statisticsRepository).getStatisticData(with(CourseOverGroundStatistic.STATISTIC_NAME), (Long) with(track.getProperty(Track.CELL_ID)));
            oneOf(statisticsRepository).putStatisticData(with(CourseOverGroundStatistic.STATISTIC_NAME), (Long) with(track.getProperty(Track.CELL_ID)), with(statistics.getMatcher()));
        }});

        // Execute
        statistic.start();
        statistic.onCellIdChanged(event);

        // Main assertations
        CourseOverGroundStatisticData capturedStatisticData = (CourseOverGroundStatisticData) statistics.getCapturedObject();

        context.assertIsSatisfied();

        TreeMap<Integer, TreeMap<Integer, TreeMap<Integer, HashMap<String, Integer>>>> data = capturedStatisticData.getData();

        assertEquals(1, data.size()); // Assert one statistic recorded
        int shipTypeBucket = data.firstKey();
        assertEquals(3, shipTypeBucket);
        int shipSizeBucket = data.get(shipTypeBucket).firstKey();
        assertEquals(3, shipSizeBucket);
        int cogBucket = data.get(shipTypeBucket).get(shipSizeBucket).firstKey();
        assertEquals(7, cogBucket);

        int numberOfStats = data.get(shipTypeBucket).get(shipSizeBucket).get(cogBucket).size();
        assertEquals(1, numberOfStats);
        String statName = data.get(shipTypeBucket).get(shipSizeBucket).get(cogBucket).keySet().iterator().next();
        Object statValue = data.get(shipTypeBucket).get(shipSizeBucket).get(cogBucket).get(statName);
        assertEquals(Integer.class, statValue.getClass());
        assertEquals(1, statValue);

        // Other assertations now we're here
        assertEquals(CourseOverGroundStatisticData.class, statistics.getCapturedObject().getClass());
        assertEquals("type", capturedStatisticData.getMeaningOfKey1());
        assertEquals("size", capturedStatisticData.getMeaningOfKey2());
        assertEquals(TreeMap.class, statistics.getCapturedObject().getData().getClass());
        assertEquals(CourseOverGroundStatisticData.STAT_SHIP_COUNT, statName);
    }

    @Test
    public void testExistingShipCountIsUpdated() {
        final CourseOverGroundStatisticData existingStatisticData = CourseOverGroundStatisticData.create();
        existingStatisticData.setValue(3-1, 3-1, 7-1, CourseOverGroundStatisticData.STAT_SHIP_COUNT, 1);

        final ArgumentCaptor<StatisticData> statistics1 = ArgumentCaptor.forClass(StatisticData.class);
        final ArgumentCaptor<StatisticData> statistics2 = ArgumentCaptor.forClass(StatisticData.class);
        context.checking(new Expectations() {{
            oneOf(trackingService).registerSubscriber(statistic);
            ignoring(statisticsService).incStatisticStatistics(with(CourseOverGroundStatistic.STATISTIC_NAME), with(any(String.class)));

            oneOf(statisticsRepository).getStatisticData(with(CourseOverGroundStatistic.STATISTIC_NAME), (Long) with(track.getProperty(Track.CELL_ID)));
            will(returnValue(null));
            oneOf(statisticsRepository).putStatisticData(with(CourseOverGroundStatistic.STATISTIC_NAME), (Long) with(track.getProperty(Track.CELL_ID)), with(statistics1.getMatcher()));

            oneOf(statisticsRepository).getStatisticData(with(CourseOverGroundStatistic.STATISTIC_NAME), (Long) with(track.getProperty(Track.CELL_ID)));
            will(returnValue(existingStatisticData));
            oneOf(statisticsRepository).putStatisticData(with(CourseOverGroundStatistic.STATISTIC_NAME), (Long) with(track.getProperty(Track.CELL_ID)), with(statistics2.getMatcher()));
        }});

        // Execute
        statistic.start();
        statistic.onCellIdChanged(event);
        statistic.onCellIdChanged(event);

        // Assert expectations and captured values
        context.assertIsSatisfied();
        // TODO assertEquals(CourseOverGroundStatistic.STATISTIC_NAME, statistics2.getCapturedObject().getStatisticName());
        assertEquals(CourseOverGroundStatisticData.class, statistics2.getCapturedObject().getClass());
        CourseOverGroundStatisticData capturedStatisticData = (CourseOverGroundStatisticData) statistics2.getCapturedObject();
        assertEquals("type", capturedStatisticData.getMeaningOfKey1());
        assertEquals("size", capturedStatisticData.getMeaningOfKey2());
        assertEquals(TreeMap.class, statistics2.getCapturedObject().getData().getClass());
        TreeMap<Integer, TreeMap<Integer, TreeMap<Integer, HashMap<String, Integer>>>> data = capturedStatisticData.getData();
        assertEquals(1, data.size()); // Assert one statistic recorded
        int shipTypeBucket = data.firstKey();
        assertEquals(3, shipTypeBucket);
        int shipSizeBucket = data.get(shipTypeBucket).firstKey();
        assertEquals(3, shipSizeBucket);
        int cogBucket = data.get(shipTypeBucket).get(shipSizeBucket).firstKey();
        assertEquals(7, cogBucket);
        int numberOfStatsForShipTypeAndShipSize = data.get(shipTypeBucket).get(shipSizeBucket).size();
        assertEquals(1, numberOfStatsForShipTypeAndShipSize);
        String statName = data.get(shipTypeBucket).get(shipSizeBucket).get(cogBucket).keySet().iterator().next();
        assertEquals(CourseOverGroundStatisticData.STAT_SHIP_COUNT, statName);
        Object statValue = data.get(shipTypeBucket).get(shipSizeBucket).get(cogBucket).get(statName);
        assertEquals(2, statValue);
    }
}