/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_MEDIA_MEDIAANALYTICSITEM_H
#define ANDROID_MEDIA_MEDIAANALYTICSITEM_H

#include <cutils/properties.h>
#include <sys/types.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
#include <utils/Timers.h>

#include <media/stagefright/foundation/AString.h>

namespace android {



class IMediaAnalyticsService;

// the class interface
//

class MediaAnalyticsItem {

    friend class MediaAnalyticsService;
    friend class IMediaAnalyticsService;
    friend class MediaMetricsJNI;
    friend class MetricsSummarizer;
    friend class MediaMetricsDeathNotifier;

    public:

            enum Type {
                kTypeNone = 0,
                kTypeInt32 = 1,
                kTypeInt64 = 2,
                kTypeDouble = 3,
                kTypeCString = 4,
                kTypeRate = 5,
            };

        // sessionid
        // unique within device, within boot,
        typedef int64_t SessionID_t;
        static constexpr SessionID_t SessionIDInvalid = -1;
        static constexpr SessionID_t SessionIDNone = 0;

        // Key: the record descriminator
        // values for the record discriminator
        // values can be "component/component"
        // basic values: "video", "audio", "drm"
        // XXX: need to better define the format
        typedef AString Key;
        static const Key kKeyNone;              // ""
        static const Key kKeyAny;               // "*"

        // Attr: names for attributes within a record
        // format "prop1" or "prop/subprop"
        // XXX: need to better define the format
        typedef const char *Attr;


        enum {
            PROTO_V0 = 0,
            PROTO_FIRST = PROTO_V0,
            PROTO_V1 = 1,
            PROTO_LAST = PROTO_V1,
        };


    public:

        // access functions for the class
        MediaAnalyticsItem();
        MediaAnalyticsItem(Key);
        ~MediaAnalyticsItem();

        // so clients can send intermediate values to be overlaid later
        MediaAnalyticsItem &setFinalized(bool);
        bool getFinalized() const;

        // SessionID ties multiple submissions for same key together
        // so that if video "height" and "width" are known at one point
        // and "framerate" is only known later, they can be be brought
        // together.
        MediaAnalyticsItem &setSessionID(SessionID_t);
        MediaAnalyticsItem &clearSessionID();
        SessionID_t getSessionID() const;
        // generates and stores a new ID iff mSessionID == SessionIDNone
        SessionID_t generateSessionID();

        // reset all contents, discarding any extra data
        void clear();
        MediaAnalyticsItem *dup();

        // set the key discriminator for the record.
        // most often initialized as part of the constructor
        MediaAnalyticsItem &setKey(MediaAnalyticsItem::Key);
        MediaAnalyticsItem::Key getKey();

        // # of attributes in the record
        int32_t count() const;

        // set values appropriately
        void setInt32(Attr, int32_t value);
        void setInt64(Attr, int64_t value);
        void setDouble(Attr, double value);
        void setRate(Attr, int64_t count, int64_t duration);
        void setCString(Attr, const char *value);

        // fused get/add/set; if attr wasn't there, it's a simple set.
        // type-mismatch counts as "wasn't there".
        void addInt32(Attr, int32_t value);
        void addInt64(Attr, int64_t value);
        void addDouble(Attr, double value);
        void addRate(Attr, int64_t count, int64_t duration);

        // find & extract values
        // return indicates whether attr exists (and thus value filled in)
        // NULL parameter value suppresses storage of value.
        bool getInt32(Attr, int32_t *value);
        bool getInt64(Attr, int64_t *value);
        bool getDouble(Attr, double *value);
        bool getRate(Attr, int64_t *count, int64_t *duration, double *rate);
        // Caller owns the returned string
        bool getCString(Attr, char **value);

        // parameter indicates whether to close any existing open
        // record with same key before establishing a new record
        // caller retains ownership of 'this'.
        bool selfrecord(bool);
        bool selfrecord();

        // remove indicated attributes and their values
        // filterNot() could also be called keepOnly()
        // return value is # attributes removed
        // XXX: perhaps 'remove' instead of 'filter'
        // XXX: filterNot would become 'keep'
        int32_t filter(int count, Attr attrs[]);
        int32_t filterNot(int count, Attr attrs[]);
        int32_t filter(Attr attr);

        // below here are used on server side or to talk to server
        // clients need not worry about these.

        // timestamp, pid, and uid only used on server side
        // timestamp is in 'nanoseconds, unix time'
        MediaAnalyticsItem &setTimestamp(nsecs_t);
        nsecs_t getTimestamp() const;

        MediaAnalyticsItem &setPid(pid_t);
        pid_t getPid() const;

        MediaAnalyticsItem &setUid(uid_t);
        uid_t getUid() const;

        MediaAnalyticsItem &setPkgName(AString);
        AString getPkgName() const;

        MediaAnalyticsItem &setPkgVersionCode(int32_t);
        int32_t getPkgVersionCode() const;

        // our serialization code for binder calls
        int32_t writeToParcel(Parcel *);
        int32_t readFromParcel(const Parcel&);

        AString toString();
        AString toString(int version);

        // are we collecting analytics data
        static bool isEnabled();

    protected:

        // merge fields from arg into this
        // with rules for first/last/add, etc
        // XXX: document semantics and how they are indicated
        // caller continues to own 'incoming'
        bool merge(MediaAnalyticsItem *incoming);

        // enabled 1, disabled 0
        static const char * const EnabledProperty;
        static const char * const EnabledPropertyPersist;
        static const int   EnabledProperty_default;

    private:

        // to help validate that A doesn't mess with B's records
        pid_t     mPid;
        uid_t     mUid;
        AString   mPkgName;
        int32_t   mPkgVersionCode;

        // let's reuse a binder connection
        static sp<IMediaAnalyticsService> sAnalyticsService;
        static sp<IMediaAnalyticsService> getInstance();
        static void dropInstance();

        // tracking information
        SessionID_t mSessionID;         // grouping similar records
        nsecs_t mTimestamp;             // ns, system_time_monotonic

        // will this record accept further updates
        bool mFinalized;

        Key mKey;

        struct Prop {

            Type mType;
            const char *mName;
            size_t mNameLen;    // the strlen(), doesn't include the null
            union {
                    int32_t int32Value;
                    int64_t int64Value;
                    double doubleValue;
                    char *CStringValue;
                    struct { int64_t count, duration; } rate;
            } u;
            void setName(const char *name, size_t len);
        };

        void initProp(Prop *item);
        void clearProp(Prop *item);
        void clearPropValue(Prop *item);
        void copyProp(Prop *dst, const Prop *src);
        enum {
            kGrowProps = 10
        };
        void growProps(int increment = kGrowProps);
        size_t findPropIndex(const char *name, size_t len);
        Prop *findProp(const char *name);
        Prop *allocateProp(const char *name);
        bool removeProp(const char *name);

        size_t mPropCount;
        size_t mPropSize;
        Prop *mProps;
};

} // namespace android

#endif
