/FileExtraction/Java_unseen/rcgroot_open-gpstracker/studio/app/src/main/java/nl/sogeti/android/gpstracker/actions/utils/StatisticsCalulator.java
/*------------------------------------------------------------------------------
 **     Ident: Sogeti Smart Mobile Solutions
 **    Author: rene
 ** Copyright: (c) Apr 24, 2011 Sogeti Nederland B.V. All Rights Reserved.
 **------------------------------------------------------------------------------
 ** Sogeti Nederland B.V.            |  No part of this file may be reproduced  
 ** Distributed Software Engineering |  or transmitted in any form or by any        
 ** Lange Dreef 17                   |  means, electronic or mechanical, for the      
 ** 4131 NJ Vianen                   |  purpose, without the express written    
 ** The Netherlands                  |  permission of the copyright holder.
 *------------------------------------------------------------------------------
 *
 *   This file is part of OpenGPSTracker.
 *
 *   OpenGPSTracker is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   OpenGPSTracker 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with OpenGPSTracker.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package nl.sogeti.android.gpstracker.actions.utils;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.os.AsyncTask;

import nl.sogeti.android.gpstracker.integration.ContentConstants.Segments;
import nl.sogeti.android.gpstracker.integration.ContentConstants.Tracks;
import nl.sogeti.android.gpstracker.integration.ContentConstants.Waypoints;
import nl.sogeti.android.gpstracker.util.Constants;
import nl.sogeti.android.gpstracker.util.UnitsI18n;

public class StatisticsCalulator extends AsyncTask<Uri, Void, Void> {
    private Context mContext;
    private String overallavgSpeedText = "Unknown";
    private String avgSpeedText = "Unknown";
    private String maxSpeedText = "Unknown";
    private String ascensionText = "Unknown";
    private String minSpeedText = "Unknown";
    private String tracknameText = "Unknown";
    private String waypointsText = "Unknown";
    private String distanceText = "Unknown";
    private long mStarttime = -1;
    private long mEndtime = -1;
    private UnitsI18n mUnits;
    private double mMaxSpeed;
    private double mMaxAltitude;
    private double mMinAltitude;
    private double mAscension;
    private double mDistanceTraveled;
    private long mDuration;
    private double mAverageActiveSpeed;
    private StatisticsDelegate mDelegate;


    public StatisticsCalulator(Context ctx, UnitsI18n units, StatisticsDelegate delegate) {
        mContext = ctx;
        mUnits = units;
        mDelegate = delegate;
    }

    /**
     * Get the overallavgSpeedText.
     *
     * @return Returns the overallavgSpeedText as a String.
     */
    public String getOverallavgSpeedText() {
        return overallavgSpeedText;
    }

    /**
     * Get the avgSpeedText.
     *
     * @return Returns the avgSpeedText as a String.
     */
    public String getAvgSpeedText() {
        return avgSpeedText;
    }

    /**
     * Get the maxSpeedText.
     *
     * @return Returns the maxSpeedText as a String.
     */
    public String getMaxSpeedText() {
        return maxSpeedText;
    }

    /**
     * Get the minSpeedText.
     *
     * @return Returns the minSpeedText as a String.
     */
    public String getMinSpeedText() {
        return minSpeedText;
    }

    /**
     * Get the tracknameText.
     *
     * @return Returns the tracknameText as a String.
     */
    public String getTracknameText() {
        return tracknameText;
    }

    /**
     * Get the waypointsText.
     *
     * @return Returns the waypointsText as a String.
     */
    public String getWaypointsText() {
        return waypointsText;
    }

    /**
     * Get the distanceText.
     *
     * @return Returns the distanceText as a String.
     */
    public String getDistanceText() {
        return distanceText;
    }

    /**
     * Get the starttime.
     *
     * @return Returns the starttime as a long.
     */
    public long getStarttime() {
        return mStarttime;
    }

    /**
     * Get the endtime.
     *
     * @return Returns the endtime as a long.
     */
    public long getEndtime() {
        return mEndtime;
    }

    /**
     * Get the maximum speed.
     *
     * @return Returns the maxSpeeddb as m/s in a double.
     */
    public double getMaxSpeed() {
        return mMaxSpeed;
    }

    /**
     * Get the min speed.
     *
     * @return Returns the average speed as m/s in a double.
     */
    public double getAverageStatisicsSpeed() {
        return mAverageActiveSpeed;
    }

    /**
     * Get the maxAltitude.
     *
     * @return Returns the maxAltitude as a double.
     */
    public double getMaxAltitude() {
        return mMaxAltitude;
    }

    /**
     * Get the minAltitude.
     *
     * @return Returns the minAltitude as a double.
     */
    public double getMinAltitude() {
        return mMinAltitude;
    }

    /**
     * Get the total ascension in m.
     *
     * @return Returns the ascension as a double.
     */
    public double getAscension() {
        return mAscension;
    }

    public CharSequence getAscensionText() {
        return ascensionText;
    }

    /**
     * Get the distanceTraveled.
     *
     * @return Returns the distanceTraveled as a float.
     */
    public double getDistanceTraveled() {
        return mDistanceTraveled;
    }

    /**
     * Get the mUnits.
     *
     * @return Returns the mUnits as a UnitsI18n.
     */
    public UnitsI18n getUnits() {
        return mUnits;
    }

    public String getDurationText() {
        long s = mDuration / 1000;
        String duration = String.format("%dh:%02dm:%02ds", s / 3600, (s % 3600) / 60, (s % 60));

        return duration;
    }

    @Override
    protected Void doInBackground(Uri... params) {
        this.updateCalculations(params[0]);
        return null;
    }

    private void updateCalculations(Uri trackUri) {
        mStarttime = -1;
        mEndtime = -1;
        mMaxSpeed = 0;
        mAverageActiveSpeed = 0;
        mMaxAltitude = 0;
        mMinAltitude = 0;
        mAscension = 0;
        mDistanceTraveled = 0f;
        mDuration = 0;
        long duration = 1;
        double ascension = 0;

        ContentResolver resolver = mContext.getContentResolver();

        Cursor waypointsCursor = null;
        try {
            waypointsCursor = resolver.query(
                    Uri.withAppendedPath(trackUri, "waypoints"),
                    new String[]{"max  (" + Waypoints.TABLE + "." + Waypoints.SPEED + ")"
                            , "max  (" + Waypoints.TABLE + "." + Waypoints.ALTITUDE + ")"
                            , "min  (" + Waypoints.TABLE + "." + Waypoints.ALTITUDE + ")"
                            , "count(" + Waypoints.TABLE + "." + Waypoints._ID + ")"},
                    null, null, null);
            if (waypointsCursor.moveToLast()) {
                mMaxSpeed = waypointsCursor.getDouble(0);
                mMaxAltitude = waypointsCursor.getDouble(1);
                mMinAltitude = waypointsCursor.getDouble(2);
                long nrWaypoints = waypointsCursor.getLong(3);
                waypointsText = nrWaypoints + "";
            }
            waypointsCursor.close();
            waypointsCursor = resolver.query(
                    Uri.withAppendedPath(trackUri, "waypoints"),
                    new String[]{"avg  (" + Waypoints.TABLE + "." + Waypoints.SPEED + ")"},
                    Waypoints.TABLE + "." + Waypoints.SPEED + "  > ?",
                    new String[]{"" + Constants.MIN_STATISTICS_SPEED},
                    null);
            if (waypointsCursor.moveToLast()) {
                mAverageActiveSpeed = waypointsCursor.getDouble(0);
            }
        } finally {
            if (waypointsCursor != null) {
                waypointsCursor.close();
            }
        }
        Cursor trackCursor = null;
        try {
            trackCursor = resolver.query(trackUri, new String[]{Tracks.NAME}, null, null, null);
            if (trackCursor.moveToLast()) {
                tracknameText = trackCursor.getString(0);
            }
        } finally {
            if (trackCursor != null) {
                trackCursor.close();
            }
        }
        Cursor segments = null;
        Location lastLocation = null;
        Location lastAltitudeLocation = null;
        Location currentLocation = null;
        try {
            Uri segmentsUri = Uri.withAppendedPath(trackUri, "segments");
            segments = resolver.query(segmentsUri, new String[]{Segments._ID}, null, null, null);
            if (segments.moveToFirst()) {
                do {
                    long segmentsId = segments.getLong(0);
                    Cursor waypoints = null;
                    try {
                        Uri waypointsUri = Uri.withAppendedPath(segmentsUri, segmentsId + "/waypoints");
                        waypoints = resolver.query(waypointsUri, new String[]{Waypoints._ID, Waypoints.TIME, Waypoints
                                .LONGITUDE, Waypoints.LATITUDE, Waypoints.ALTITUDE}, null, null, null);
                        if (waypoints.moveToFirst()) {
                            do {
                                if (mStarttime < 0) {
                                    mStarttime = waypoints.getLong(1);
                                }
                                currentLocation = new Location(this.getClass().getName());
                                currentLocation.setTime(waypoints.getLong(1));
                                currentLocation.setLongitude(waypoints.getDouble(2));
                                currentLocation.setLatitude(waypoints.getDouble(3));
                                currentLocation.setAltitude(waypoints.getDouble(4));

                                // Do no include obvious wrong 0.0 lat 0.0 long, skip to next value in while-loop
                                if (currentLocation.getLatitude() == 0.0d || currentLocation.getLongitude() == 0.0d) {
                                    continue;
                                }

                                if (lastLocation != null) {
                                    float travelPart = lastLocation.distanceTo(currentLocation);
                                    long timePart = currentLocation.getTime() - lastLocation.getTime();
                                    mDistanceTraveled += travelPart;
                                    duration += timePart;
                                }
                                if (currentLocation.hasAltitude()) {
                                    if (lastAltitudeLocation != null) {
                                        if (currentLocation.getTime() - lastAltitudeLocation.getTime() > 5 * 60 * 1000) // more
                                        // then a 5m of climbing
                                        {
                                            if (currentLocation.getAltitude() > lastAltitudeLocation.getAltitude() + 1) // more
                                            // then 1m climb
                                            {
                                                ascension += currentLocation.getAltitude() - lastAltitudeLocation.getAltitude();
                                                lastAltitudeLocation = currentLocation;
                                            } else {
                                                lastAltitudeLocation = currentLocation;
                                            }
                                        }
                                    } else {
                                        lastAltitudeLocation = currentLocation;
                                    }
                                }
                                lastLocation = currentLocation;
                                mEndtime = lastLocation.getTime();
                            }
                            while (waypoints.moveToNext());
                            mDuration = mEndtime - mStarttime;
                        }
                    } finally {
                        if (waypoints != null) {
                            waypoints.close();
                        }
                    }
                    lastLocation = null;
                }
                while (segments.moveToNext());
            }
        } finally {
            if (segments != null) {
                segments.close();
            }
        }
        double maxSpeed = mUnits.conversionFromMetersPerSecond(mMaxSpeed);
        double overallavgSpeedfl = mUnits.conversionFromMeterAndMiliseconds(mDistanceTraveled, mDuration);
        double avgSpeedfl = mUnits.conversionFromMeterAndMiliseconds(mDistanceTraveled, duration);
        double traveled = mUnits.conversionFromMeter(mDistanceTraveled);
        avgSpeedText = mUnits.formatSpeed(avgSpeedfl, true);
        overallavgSpeedText = mUnits.formatSpeed(overallavgSpeedfl, true);
        maxSpeedText = mUnits.formatSpeed(maxSpeed, true);
        distanceText = String.format("%.2f %s", traveled, mUnits.getDistanceUnit());
        ascensionText = String.format("%.0f %s", ascension, mUnits.getHeightUnit());
    }

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
        if (mDelegate != null) {
            mDelegate.finishedCalculations(this);
        }

    }
}
