package au.gov.amsa.geo.adhoc;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.davidmoten.rx.Functions;

import au.gov.amsa.ais.ShipTypeDecoder;
import au.gov.amsa.gt.Shapefile;
import au.gov.amsa.navigation.ShipStaticData;
import au.gov.amsa.navigation.ShipStaticData.Info;
import au.gov.amsa.risky.format.BinaryFixes;
import au.gov.amsa.risky.format.Fix;
import au.gov.amsa.util.Files;
import rx.Observable;
import rx.functions.Func2;

public class LMSAdhocMain {

    private static final Logger log = LoggerFactory.getLogger(LMSAdhocMain.class);

    public static void main(String[] args) throws FileNotFoundException, IOException {
        Map<Integer, Info> ships = ShipStaticData.getMapFromResource("/ship-data-2014.txt");
        Pattern pattern = Pattern.compile(".*\\.track");
        File base = new File("/media/an/binary-fixes-lms2");
        List<File> files = Files.find(new File(base, "2016"), pattern);
        log.info("files=" + files.size());

        Func2<Fix, Fix, Integer> ascendingTime = (a, b) -> ((Long) a.time()).compareTo(b.time());

        String[] shapes = new String[] { "fremantle_port_limits_pl.zip",
                "dampier_port_limits_pl.zip", "port_hedland_port_limits_pl.zip" };
        File shapeBase = new File("/media/an/shapefiles/port-boundaries");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm")
                .withZone(ZoneOffset.UTC);
        System.out.println("region, mmsi, imo, class, type, lat, lon, timeUTC");
        for (String shape : shapes) {
            final String shapeName = shape.replace("_port_limits_pl.zip", "");
            try (InputStream is = new FileInputStream(new File(shapeBase, shape))) {
                Shapefile region = Shapefile.fromZip(is);
                Observable.from(files)
                        // only international vessels
                        .filter(file -> !file.getName().startsWith("503"))
                        //
                        // .filter(file ->
                        // file.getName().startsWith("215040000")
                        // || file.getName().startsWith("351424000"))
                        // .doOnNext(System.out::println)
                        .concatMap(file -> detectCrossings(file, region))
                        // count out to in crossings
                        .toSortedList(ascendingTime) //
                        .flatMapIterable(Functions.identity()) //
                        // log
                        .doOnNext(fix -> {
                            Info info = ships.get(fix.mmsi());
                            String type = "";
                            if (info != null) {
                                if (info.shipType.isPresent())
                                    type = ShipTypeDecoder.getShipType(info.shipType.get());
                                else
                                    type = "";
                            }
                            String t = formatter
                                    .format(ZonedDateTime.ofInstant(
                                            Instant.ofEpochMilli(fix.time()), ZoneOffset.UTC))
                                    .replace("[UTC]", "");
                            String imo = info == null ? "" : info.imo.orElse("");
                            System.out.format("%s,%s,%s,%s,\"%s\",%s,%s,%s\n", shapeName,
                                    fix.mmsi(), imo, fix.aisClass().name(), type, fix.lat(),
                                    fix.lon(), t);
                        }).count().toBlocking().single();
            }
        }

    }

    private static final DateTimeFormatter date = DateTimeFormatter.ofPattern("dd/MM/yyyy")
            .withZone(ZoneOffset.UTC);

    private static Observable<Fix> detectCrossings(File file, Shapefile region) {
        return BinaryFixes.from(file)
                // log
                // .lift(Logging.<Fix> logger().onCompleted("read " +
                // file).every(10000000)
                // .showValue(false).log())
                //
                .filter(fix -> fix.mmsi() != 0)
                // add in or out of region
                .map(fix -> new FixAndRegion(fix, region.contains(fix.lat(), fix.lon())))
                // detect changes in being in
                // our out of region
                .distinctUntilChanged(fix -> fix.inRegion)
                // pair them up
                .buffer(2)
                // just get out of region to
                // inside region
                .filter(list -> list.size() == 2 && !list.get(0).inRegion && list.get(1).inRegion)
                // get the fix inside the region
                .map(list -> list.get(1).fix)
                // one per day
                .distinctUntilChanged(fix -> date.format(
                        ZonedDateTime.ofInstant(Instant.ofEpochMilli(fix.time()), ZoneOffset.UTC)));
    }

    private static class FixAndRegion {
        final Fix fix;
        final boolean inRegion;

        FixAndRegion(Fix fix, boolean inRegion) {
            super();
            this.fix = fix;
            this.inRegion = inRegion;
        }

    }

}
