package org.openstreetmap.josm.plugins.editgpx.data;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.TimeZone;

import org.openstreetmap.josm.data.gpx.GpxTrack;
import org.openstreetmap.josm.data.gpx.GpxTrackSegment;
import org.openstreetmap.josm.data.gpx.ImmutableGpxTrack;
import org.openstreetmap.josm.data.gpx.WayPoint;

public class EditGpxTrack {

    private final List<EditGpxTrackSegment> segments = new ArrayList<>();
    private final Map<String, Object> attributes = new HashMap<>();
    private boolean isDeleted;

    public EditGpxTrack(GpxTrack track) {
        attributes.putAll(track.getAttributes());
        for (GpxTrackSegment segment: track.getSegments()) {
            segments.add(new EditGpxTrackSegment(segment));
        }
    }

    public List<EditGpxTrackSegment> getSegments() {
        return segments;
    }
    public Map<String, Object> getAttributes() {
        return attributes;
    }

    public GpxTrack createGpxTrack(boolean anonTime, double minTime) {

        Collection<Collection<WayPoint>> wayPoints = new ArrayList<>();

        final DateFormat iso8601 =
            new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        final TimeZone utc = TimeZone.getTimeZone("UTC");
        iso8601.setTimeZone(utc);

        for (EditGpxTrackSegment segment: segments) {
            if (!segment.isDeleted()) {
                List<WayPoint> points = segment.getNonDeletedWaypoints();
                if (!points.isEmpty()) {
                    if (anonTime) {
                        // convert to anonymous time
                        for (WayPoint w : points) {
                            double t = w.time - minTime;
                            w.attr.put("time", iso8601.format(
                                    new Date((long)(t * 1000))));
                            w.setTime();
                            assert w.time == t;
                            if (w.attr.containsKey("name")) {
                                w.attr.put("name", "anon"); //time information can also be in "name" field. so delete time information
                            }
                        }
                    }
                    wayPoints.add(points);
                }
            }
        }
        if (anonTime) {
            if (attributes.containsKey("name")) {
                attributes.put("name", "anon");//time information can also be in "name" field. so delete time information
            }
        }

        return new ImmutableGpxTrack(wayPoints, attributes);
    }

    public void setDeleted(boolean isDeleted) {
        this.isDeleted = isDeleted;
    }

    public boolean isDeleted() {
        return isDeleted;
    }

    /**
     * time of the oldest waypoint in the set of non-deleted waypoints
     * in this track (in seconds since Epoch)
     */
    public double minNonDeletedTime() {
        boolean foundOne = false;
        double minTime = 0.0;

        for (EditGpxTrackSegment segment: segments) {
            if (!segment.isDeleted()) {
                try {
                    double t = segment.minNonDeletedTime();
                    if ((!foundOne) || (t < minTime)) {
                        minTime = t;
                    }
                    foundOne = true;
                } catch (NoSuchElementException e) {
                    continue;
                }
            }
        }

        if (!foundOne) {
            throw new NoSuchElementException();
        }
        return minTime;
    }
}
