/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is MPEG4IP.
 *
 * The Initial Developer of the Original Code is Cisco Systems Inc.
 * Portions created by Cisco Systems Inc. are
 * Copyright (C) Cisco Systems Inc. 2004.  All Rights Reserved.
 *
 * Contributor(s):
 *      Bill May (from others work).
 */

#include "src/impl.h"

namespace mp4v2 { namespace impl {

///////////////////////////////////////////////////////////////////////////////

MP4StandardAtom::MP4StandardAtom (MP4File &file, const char *type) : MP4Atom(file, type)
{
    /*
     * This is a big if else loop.  Make sure that you don't break it
     * when adding new atoms, or you will set the unknown type flag
     *
     * Try to keep it in alphabetical order - it should only be called
     * 1 time per atom, so it's not that urgent.
     */
    if (ATOMID(type) == ATOMID("bitr")) {
        AddProperty( /* 0 */
            new MP4Integer32Property(*this, "avgBitrate"));

        AddProperty( /* 1 */
            new MP4Integer32Property(*this, "maxBitrate"));

    } else if (ATOMID(type) == ATOMID("btrt")) {
        AddProperty( new MP4Integer32Property(*this, "bufferSizeDB")); /* 0 */
        AddProperty( new MP4Integer32Property(*this, "avgBitrate"));   /* 1 */
        AddProperty( new MP4Integer32Property(*this, "maxBitrate"));   /* 2 */
    } else if (ATOMID(type) == ATOMID("burl")) {
        AddProperty( new MP4StringProperty(*this, "base_url"));
        /*
         * c???
         */
    } else if (ATOMID(type) == ATOMID("co64")) {
        AddVersionAndFlags();

        MP4Integer32Property* pCount =
            new MP4Integer32Property(*this, "entryCount");
        AddProperty(pCount);

        MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
        AddProperty(pTable);

        pTable->AddProperty(
            new MP4Integer64Property(*this, "chunkOffset"));
    } else if (ATOMID(type) == ATOMID("ctts")) {
        AddVersionAndFlags();

        MP4Integer32Property* pCount =
            new MP4Integer32Property(*this, "entryCount");
        AddProperty(pCount);

        MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
        AddProperty(pTable);

        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "sampleCount"));
        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "sampleOffset"));
        /*
         * d???
         */
    } else if (ATOMID(type) == ATOMID("dinf")) {
        ExpectChildAtom("dref", Required, OnlyOne);

    } else if (ATOMID(type) == ATOMID("dimm")) {
        AddProperty( // bytes of immediate data
            new MP4Integer64Property(*this, "bytes"));

    } else if (ATOMID(type) == ATOMID("dmax")) {
        AddProperty( // max packet duration
            new MP4Integer32Property(*this, "milliSecs"));

    } else if (ATOMID(type) == ATOMID("dmed")) {
        AddProperty( // bytes sent from media data
            new MP4Integer64Property(*this, "bytes"));

    } else if (ATOMID(type) == ATOMID("drep")) {
        AddProperty( // bytes of repeated data
            new MP4Integer64Property(*this, "bytes"));
        /*
         * e???
         */
    } else if (ATOMID(type) == ATOMID("edts")) {
        ExpectChildAtom("elst", Required, OnlyOne);

    } else if (ATOMID(type) == ATOMID("esds")) {
        AddVersionAndFlags();
        AddProperty(
            new MP4DescriptorProperty(*this, NULL, MP4ESDescrTag, 0,
                                      Required, OnlyOne));
        /*
         * f???
         */
    } else if (ATOMID(type) == ATOMID("frma")) {
        AddProperty( /* 0 */
            new MP4Integer32Property(*this, "data-format"));
        /*
         * g???
         */
    } else if (ATOMID(type) == ATOMID("gmhd")) {
        ExpectChildAtom("gmin", Required, OnlyOne);
        ExpectChildAtom("tmcd", Optional, OnlyOne);
        ExpectChildAtom("text", Optional, OnlyOne);
    } else if (ATOMID(type) == ATOMID("hmhd")) {
        AddVersionAndFlags();

        AddProperty(new MP4Integer16Property(*this, "maxPduSize"));
        AddProperty(new MP4Integer16Property(*this, "avgPduSize"));
        AddProperty(new MP4Integer32Property(*this, "maxBitRate"));
        AddProperty(new MP4Integer32Property(*this, "avgBitRate"));
        AddProperty(new MP4Integer32Property(*this, "slidingAvgBitRate"));
        /*
         * i???
         */
    } else if (ATOMID(type) == ATOMID("iKMS")) {
        AddVersionAndFlags(); /* 0, 1 */
        MP4StringProperty* pProp = new MP4StringProperty(*this, "kms_URI");
        AddProperty(pProp); /* 2 */

    } else if (ATOMID(type) == ATOMID("iSFM")) {
        AddVersionAndFlags(); /* 0, 1 */
        AddProperty( /* 2 */
            new MP4BitfieldProperty(*this, "selective-encryption", 1));
        AddProperty( /* 3 */
            new MP4BitfieldProperty(*this, "reserved", 7));
        AddProperty( /* 4 */
            new MP4Integer8Property(*this, "key-indicator-length"));
        AddProperty( /* 5 */
            new MP4Integer8Property(*this, "IV-length"));

    } else if (ATOMID(type) == ATOMID("ilst")) {
        ExpectChildAtom("\251nam", Optional, OnlyOne); /* name */
        ExpectChildAtom("\251ART", Optional, OnlyOne); /* artist */
        ExpectChildAtom("\251wrt", Optional, OnlyOne); /* writer */
        ExpectChildAtom("\251alb", Optional, OnlyOne); /* album */
        ExpectChildAtom("\251day", Optional, OnlyOne); /* date */
        ExpectChildAtom("\251too", Optional, OnlyOne); /* tool */
        ExpectChildAtom("\251cmt", Optional, OnlyOne); /* comment */
        ExpectChildAtom("\251gen", Optional, OnlyOne); /* custom genre */
        ExpectChildAtom("trkn", Optional, OnlyOne); /* tracknumber */
        ExpectChildAtom("disk", Optional, OnlyOne); /* disknumber */
        ExpectChildAtom("gnre", Optional, OnlyOne); /* genre (ID3v1 index + 1) */
        ExpectChildAtom("cpil", Optional, OnlyOne); /* compilation */
        ExpectChildAtom("tmpo", Optional, OnlyOne); /* BPM */
        ExpectChildAtom("covr", Optional, OnlyOne); /* cover art */
        ExpectChildAtom("aART", Optional, OnlyOne); /* album artist */
        ExpectChildAtom("----", Optional, Many); /* ---- free form */
        ExpectChildAtom("pgap", Optional, OnlyOne); /* part of gapless album */
        ExpectChildAtom("tvsh", Optional, OnlyOne); /* TV show */
        ExpectChildAtom("tvsn", Optional, OnlyOne); /* TV season */
        ExpectChildAtom("tven", Optional, OnlyOne); /* TV episode number */
        ExpectChildAtom("tvnn", Optional, OnlyOne); /* TV network name */
        ExpectChildAtom("tves", Optional, OnlyOne); /* TV epsidoe */
        ExpectChildAtom("desc", Optional, OnlyOne); /* description */
        ExpectChildAtom("ldes", Optional, OnlyOne); /* long description */
        ExpectChildAtom("soal", Optional, OnlyOne); /* sort album */
        ExpectChildAtom("soar", Optional, OnlyOne); /* sort artist */
        ExpectChildAtom("soaa", Optional, OnlyOne); /* sort album artist */
        ExpectChildAtom("sonm", Optional, OnlyOne); /* sort name */
        ExpectChildAtom("soco", Optional, OnlyOne); /* sort composer */
        ExpectChildAtom("sosn", Optional, OnlyOne); /* sort show */
        ExpectChildAtom("hdvd", Optional, OnlyOne); /* HD video */
        ExpectChildAtom("Šenc", Optional, OnlyOne); /* Encoded by */
        ExpectChildAtom("pcst", Optional, OnlyOne); /* Podcast flag */
        ExpectChildAtom("keyw", Optional, OnlyOne); /* Keywords (for podcasts?) */
        ExpectChildAtom("catg", Optional, OnlyOne); /* Category (for podcasts?) */
        ExpectChildAtom("purl", Optional, OnlyOne); /* Podcast URL */
        ExpectChildAtom("egid", Optional, OnlyOne); /* Podcast episode global unique ID */
        ExpectChildAtom("rtng", Optional, OnlyOne); /* Content Rating */
        ExpectChildAtom("stik", Optional, OnlyOne); /* MediaType */
        ExpectChildAtom("\251grp", Optional, OnlyOne); /* Grouping */
        ExpectChildAtom("\251lyr", Optional, OnlyOne); /* Lyrics */
        ExpectChildAtom("cprt", Optional, OnlyOne); /* Copyright */
        ExpectChildAtom("apID", Optional, OnlyOne); /* iTunes Account */
        ExpectChildAtom("akID", Optional, OnlyOne); /* iTunes Account Type */
        ExpectChildAtom("sfID", Optional, OnlyOne); /* iTunes Country */
        ExpectChildAtom("cnID", Optional, OnlyOne); /* Content ID */
        ExpectChildAtom("atID", Optional, OnlyOne); /* Artist ID */
        ExpectChildAtom("plID", Optional, OnlyOne); /* Playlist ID */
        ExpectChildAtom("geID", Optional, OnlyOne); /* Genre ID */
        ExpectChildAtom("cmID", Optional, OnlyOne); /* Composer ID */
        ExpectChildAtom("xid ", Optional, OnlyOne); /* XID */

    }  else if (ATOMID(type) == ATOMID("imif")) {
        AddVersionAndFlags();
        AddProperty(new MP4DescriptorProperty(*this, "ipmp_desc", MP4IPMPDescrTag,
                                              MP4IPMPDescrTag, Required, Many));
    } else if (ATOMID(type) == ATOMID("iods")) {
        AddVersionAndFlags();
        AddProperty(
            new MP4DescriptorProperty(*this, NULL, MP4FileIODescrTag,
                                      MP4FileODescrTag,
                                      Required, OnlyOne));
        /*
         * m???
         */
    } else if (ATOMID(type) == ATOMID("maxr")) {
        AddProperty(new MP4Integer32Property(*this, "granularity"));
        AddProperty(new MP4Integer32Property(*this, "bytes"));

    } else if (ATOMID(type) == ATOMID("mdia")) {
        ExpectChildAtom("mdhd", Required, OnlyOne);
        ExpectChildAtom("hdlr", Required, OnlyOne);
        ExpectChildAtom("minf", Required, OnlyOne);

    } else if (ATOMID(type) == ATOMID("meta")) { // iTunes
        AddVersionAndFlags(); /* 0, 1 */
        ExpectChildAtom("hdlr", Required, OnlyOne);
        ExpectChildAtom("ilst", Required, OnlyOne);

    } else if (ATOMID(type) == ATOMID("mfhd")) {
        AddVersionAndFlags();   /* 0, 1 */
        AddProperty( /* 2 */
            new MP4Integer32Property(*this, "sequenceNumber"));

    } else if (ATOMID(type) == ATOMID("minf")) {
        ExpectChildAtom("vmhd", Optional, OnlyOne);
        ExpectChildAtom("smhd", Optional, OnlyOne);
        ExpectChildAtom("hmhd", Optional, OnlyOne);
        ExpectChildAtom("nmhd", Optional, OnlyOne);
        ExpectChildAtom("gmhd", Optional, OnlyOne);
        ExpectChildAtom("dinf", Required, OnlyOne);
        ExpectChildAtom("stbl", Required, OnlyOne);

    } else if (ATOMID(type) == ATOMID("moof")) {
        ExpectChildAtom("mfhd", Required, OnlyOne);
        ExpectChildAtom("traf", Optional, Many);

    } else if (ATOMID(type) == ATOMID("moov")) {
        ExpectChildAtom("mvhd", Required, OnlyOne);
        ExpectChildAtom("iods", Optional, OnlyOne);
        ExpectChildAtom("trak", Required, Many);
        ExpectChildAtom("udta", Optional, Many);
        ExpectChildAtom("mvex", Optional, OnlyOne);

    } else if (ATOMID(type) == ATOMID("mvex")) {
        ExpectChildAtom("trex", Required, Many);

        /*
         * n???
         */
    } else if (ATOMID(type) == ATOMID("nmhd")) {
        AddVersionAndFlags();

    } else if (ATOMID(type) == ATOMID("nump")) {
        AddProperty( // packets sent
            new MP4Integer64Property(*this, "packets"));
        /*
          * o???
          */
    } else if (ATOMID(type) == ATOMID("odkm")) {
        AddVersionAndFlags();
        ExpectChildAtom("ohdr", Required, OnlyOne);
        /*
         * p???
         */
    } else if (ATOMID(type) == ATOMID("payt")) {
        AddProperty(new MP4Integer32Property(*this, "payloadNumber"));
        AddProperty(new MP4StringProperty(*this, "rtpMap", Counted));

    } else if (ATOMID(type) == ATOMID("pinf")) {
        ExpectChildAtom("frma", Required, OnlyOne);
    } else if (ATOMID(type) == ATOMID("pmax")) {
        AddProperty( // max packet size
            new MP4Integer32Property(*this, "bytes"));
    } else if (ATOMID(type) == ATOMID("schi")) {
        // not sure if this is child atoms or table of boxes
        // get clarification on spec 9.1.2.5
        ExpectChildAtom("odkm", Optional, OnlyOne);
        ExpectChildAtom("iKMS", Optional, OnlyOne);
        ExpectChildAtom("iSFM", Optional, OnlyOne);

    } else if (ATOMID(type) == ATOMID("schm")) {
        AddVersionAndFlags(); /* 0, 1 */
        AddProperty( /* 2 */
            new MP4Integer32Property(*this, "scheme_type"));
        AddProperty( /* 3 */
            new MP4Integer32Property(*this, "scheme_version"));
        // browser URI if flags set, TODO

    } else if (ATOMID(type) == ATOMID("sinf")) {
        ExpectChildAtom("frma", Required, OnlyOne);
        ExpectChildAtom("imif", Optional, OnlyOne);
        ExpectChildAtom("schm", Optional, OnlyOne);
        ExpectChildAtom("schi", Optional, OnlyOne);

    } else if (ATOMID(type) == ATOMID("smhd")) {
        AddVersionAndFlags();
        AddReserved(*this, "reserved", 4);

    } else if (ATOMID(type) == ATOMID("snro")) {
        AddProperty(new MP4Integer32Property(*this, "offset"));

    } else if (ATOMID(type) == ATOMID("stco")) {
        AddVersionAndFlags();

        MP4Integer32Property* pCount = new MP4Integer32Property(*this, "entryCount");
        AddProperty(pCount);

        MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
        AddProperty(pTable);

        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "chunkOffset"));

    } else if (ATOMID(type) == ATOMID("stsh")) {
        AddVersionAndFlags();

        MP4Integer32Property* pCount = new MP4Integer32Property(*this, "entryCount");
        AddProperty(pCount);

        MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
        AddProperty(pTable);

        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "shadowedSampleNumber"));
        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "syncSampleNumber"));

    } else if (ATOMID(type) == ATOMID("stss")) {
        AddVersionAndFlags();

        MP4Integer32Property* pCount = new MP4Integer32Property(*this, "entryCount");
        AddProperty(pCount);

        MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
        AddProperty(pTable);

        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "sampleNumber"));

    } else if (ATOMID(type) == ATOMID("stts")) {
        AddVersionAndFlags();
        MP4Integer32Property* pCount = new MP4Integer32Property(*this, "entryCount");
        AddProperty(pCount);

        MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
        AddProperty(pTable);

        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "sampleCount"));
        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "sampleDelta"));
    } else if (ATOMID(type) == ATOMID("tims")) {
        AddProperty(
            new MP4Integer32Property(*this, "timeScale"));

    } else if (ATOMID(type) == ATOMID("tmin")) {
        AddProperty( // min relative xmit time
            new MP4Integer32Property(*this, "milliSecs"));

    } else if (ATOMID(type) == ATOMID("tmax")) {
        AddProperty( // max relative xmit time
            new MP4Integer32Property(*this, "milliSecs"));

    } else if (ATOMID(type) == ATOMID("traf")) {
        ExpectChildAtom("tfhd", Required, OnlyOne);
        ExpectChildAtom("trun", Optional, Many);

    } else if (ATOMID(type) == ATOMID("trak")) {
        ExpectChildAtom("tkhd", Required, OnlyOne);
        ExpectChildAtom("tref", Optional, OnlyOne);
        ExpectChildAtom("edts", Optional, OnlyOne);
        ExpectChildAtom("mdia", Required, OnlyOne);
        ExpectChildAtom("udta", Optional, Many);

    } else if (ATOMID(type) == ATOMID("tref")) {
        ExpectChildAtom("chap", Optional, OnlyOne);
        ExpectChildAtom("dpnd", Optional, OnlyOne);
        ExpectChildAtom("hint", Optional, OnlyOne);
        ExpectChildAtom("ipir", Optional, OnlyOne);
        ExpectChildAtom("mpod", Optional, OnlyOne);
        ExpectChildAtom("sync", Optional, OnlyOne);

    } else if (ATOMID(type) == ATOMID("trex")) {
        AddVersionAndFlags();   /* 0, 1 */
        AddProperty( /* 2 */
            new MP4Integer32Property(*this, "trackId"));
        AddProperty( /* 3 */
            new MP4Integer32Property(*this, "defaultSampleDesriptionIndex"));
        AddProperty( /* 4 */
            new MP4Integer32Property(*this, "defaultSampleDuration"));
        AddProperty( /* 5 */
            new MP4Integer32Property(*this, "defaultSampleSize"));
        AddProperty( /* 6 */
            new MP4Integer32Property(*this, "defaultSampleFlags"));

    } else if (ATOMID(type) == ATOMID("trpy") ||
               ATOMID(type) == ATOMID("tpyl")) {
        AddProperty( // bytes sent including RTP headers
            new MP4Integer64Property(*this, "bytes"));

    } else if (ATOMID(type) == ATOMID("tsro")) {
        AddProperty(
            new MP4Integer32Property(*this, "offset"));
    } else if (ATOMID(type) == ATOMID("wave")) {
        ExpectChildAtom("esds", Required, OnlyOne);
    } else {
        /*
         * default - unknown type
         */
        SetUnknownType(true);
    }
}

///////////////////////////////////////////////////////////////////////////////

}} // namespace mp4v2::impl
