/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "ExSurfaceFlinger.h"
#include "ExLayer.h"
#include <fstream>
#include <cutils/properties.h>
#ifdef QTI_BSP
#include <hardware/display_defs.h>
#endif

#define ATRACE_TAG ATRACE_TAG_GRAPHICS

namespace android {

bool ExSurfaceFlinger::sExtendedMode = false;

ExSurfaceFlinger::ExSurfaceFlinger() {
    char property[PROPERTY_VALUE_MAX] = {0};

    mDebugLogs = false;
    if((property_get("persist.debug.qdframework.logs", property, NULL) > 0) &&
       (!strncmp(property, "1", PROPERTY_VALUE_MAX ) ||
        (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) {
        mDebugLogs = true;
    }

    ALOGD_IF(isDebug(),"Creating custom SurfaceFlinger %s",__FUNCTION__);

    mDisableExtAnimation = false;
    if((property_get("sys.disable_ext_animation", property, "0") > 0) &&
       (!strncmp(property, "1", PROPERTY_VALUE_MAX ) ||
        (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) {
        mDisableExtAnimation = true;
    }

    ALOGD_IF(isDebug(),"Animation on external is %s in %s",
             mDisableExtAnimation ? "disabled" : "not disabled", __FUNCTION__);
}

ExSurfaceFlinger::~ExSurfaceFlinger() { }

void ExSurfaceFlinger::updateExtendedMode() {
    char prop[PROPERTY_VALUE_MAX];
    property_get("sys.extended_mode", prop, "0");
    sExtendedMode = atoi(prop) ? true : false;
}

void ExSurfaceFlinger::getIndexLOI(size_t dpy,
                               const LayerVector& currentLayers,
                               bool& bIgnoreLayers,
                               int& indexLOI ) {
    size_t i = currentLayers.size();
    while(i--) {
        const sp<Layer>& layer = currentLayers[i];
        /* iterate through the layer list to find ext_only layers and store
         * the index
         */
        if (layer->isSecureDisplay()) {
            bIgnoreLayers = true;
            indexLOI = -1;
            if(!dpy)
                indexLOI = i;
            break;
        }
        /* iterate through the layer list to find ext_only layers or yuv
         * layer(extended_mode) and store the index
         */
        if ((dpy && (layer->isExtOnly() ||
                     (isExtendedMode() && layer->isYuvLayer())))) {
            bIgnoreLayers= true;
            indexLOI = i;
        }
    }
    return;
}

bool ExSurfaceFlinger::updateLayerVisibleNonTransparentRegion(
        const int& dpy, const sp<Layer>& layer,
        bool& bIgnoreLayers, int& indexLOI,
        uint32_t layerStack, const int& i) {

    const Layer::State& s(layer->getDrawingState());

    /* Only add the layer marked as "external_only" or yuvLayer
     * (extended_mode) to external list and
     * only remove the layer marked as "external_only" or yuvLayer in
     * extended_mode from primary list
     * and do not add the layer marked as "internal_only" to external list
     * Add secure UI layers to primary and remove other layers from internal
     * and external list
     */
    if(((bIgnoreLayers && indexLOI != (int)i) ||
        (!dpy && layer->isExtOnly()) ||
        (!dpy && isExtendedMode() && layer->isYuvLayer()))||
       (dpy && layer->isIntOnly())) {
        /* Ignore all other layers except the layers marked as ext_only
         * by setting visible non transparent region empty
         */
        Region visibleNonTransRegion;
        visibleNonTransRegion.set(Rect(0,0));
        layer->setVisibleNonTransparentRegion(visibleNonTransRegion);
        return true;
    }
    /* only consider the layers on the given later stack
     * Override layers created using presentation class by the layers having
     * ext_only flag enabled
     */
    if(s.layerStack != layerStack && !bIgnoreLayers) {
        /* set the visible region as empty since we have removed the
         * layerstack check in rebuildLayerStack() function
         */
        Region visibleNonTransRegion;
        visibleNonTransRegion.set(Rect(0,0));
        layer->setVisibleNonTransparentRegion(visibleNonTransRegion);
        return true;
    }

    if (mDisableExtAnimation) {
        /* Remove screenShotSurface from secondary displays when ext animation disabled */
        const int screenShotLen = strlen("ScreenshotSurface");
        if (dpy && !strncmp(layer->getName(), "ScreenshotSurface", screenShotLen) ) {
            Region visibleNonTransRegion;
            visibleNonTransRegion.set(Rect(0, 0));
            layer->setVisibleNonTransparentRegion(visibleNonTransRegion);
            return true;
        }
    }

    return false;
}

void ExSurfaceFlinger::delayDPTransactionIfNeeded(
        const Vector<DisplayState>& displays) {
    /* Delay the display projection transaction by 50ms only when the disable
     * external rotation animation feature is enabled
     */
    if(mDisableExtAnimation) {
        size_t count = displays.size();
        for (size_t i=0 ; i<count ; i++) {
            const DisplayState& s(displays[i]);
            if((mDisplays.indexOfKey(s.token) >= 0) && (s.token !=
                    mBuiltinDisplays[DisplayDevice::DISPLAY_PRIMARY])) {
                const uint32_t what = s.what;
                /* Invalidate and Delay the binder thread by 50 ms on
                 * eDisplayProjectionChanged to trigger a draw cycle so that
                 * it can fix one incorrect frame on the External, when we
                 * disable external animation
                 */
                if (what & DisplayState::eDisplayProjectionChanged) {
                    invalidateHwcGeometry();
                    repaintEverything();
                    usleep(50000);
                }
            }
        }
    }
}

bool ExSurfaceFlinger::canDrawLayerinScreenShot(
                             const sp<const DisplayDevice>& hw,
                             const sp<Layer>& layer) {
    int dispType = hw->getDisplayType();
    /* a) Don't draw SecureDisplayLayer or ProtectedLayer.
     * b) Don't let ext_only and extended_mode to be captured
     * If not, we would see incorrect image during rotation
     * on primary.
     */
    if(!layer->isSecureDisplay()
       && !layer->isProtected()
       && !(!dispType && (layer->isExtOnly() ||
          (isExtendedMode() && layer->isYuvLayer())))
       && !(layer->isIntOnly() && dispType)
       && layer->isVisible()){
        return true;
    }
    return false;
}

void ExSurfaceFlinger::isfreezeSurfacePresent(bool& freezeSurfacePresent,
                             const sp<const DisplayDevice>& hw,
                             const int32_t& id) {
    freezeSurfacePresent = false;
    /* Get the layers in the current drawing state */
    const LayerVector& layers(mDrawingState.layersSortedByZ);
    const size_t layerCount = layers.size();
    /* Look for ScreenShotSurface in external layer list */
    if(id != HWC_DISPLAY_PRIMARY) {
        for (size_t i = 0 ; i < layerCount ; ++i) {
            static int screenShotLen = strlen("ScreenshotSurface");
            const sp<Layer>& layer(layers[i]);
            const Layer::State& s(layer->getDrawingState());
            /* check the layers associated with external display */
            if(s.layerStack == hw->getLayerStack()) {
                if(!strncmp(layer->getName(), "ScreenshotSurface",
                            screenShotLen)) {
                    /* Screenshot layer is present, and animation in
                     * progress
                     */
                    freezeSurfacePresent = true;
                    break;
                }
            }
        }
    }
}

void ExSurfaceFlinger::setOrientationEventControl(bool& freezeSurfacePresent,
                             const int32_t& id) {
    HWComposer& hwc(getHwComposer());
    HWComposer::LayerListIterator cur = hwc.begin(id);
    if(!cur)
        return;

    if(freezeSurfacePresent) {
        /* If freezeSurfacePresent, set ANIMATING flag
         * which is used to support disable animation on external
         */
        cur->setAnimating(true);
    }
}

void ExSurfaceFlinger::updateVisibleRegionsDirty() {
    /* If extended_mode is set, and set mVisibleRegionsDirty
     * as we need to rebuildLayerStack
     */
    if(isExtendedMode()) {
        mVisibleRegionsDirty = true;
    }
}

void ExSurfaceFlinger::drawWormHoleIfRequired(HWComposer::LayerListIterator& cur,
        const HWComposer::LayerListIterator& end,
        const sp<const DisplayDevice>& hw,
        const Region& region) {
    if (cur != end) {
#ifdef QTI_BSP
        if (cur->getCompositionType() != HWC_BLIT)
            drawWormhole(hw, region);
#endif
    } else {
           drawWormhole(hw, region);
    }
}

#if (defined QTI_BSP) && (defined QTI_S3D)
bool ExSurfaceFlinger::isS3DLayerPresent(const sp<const DisplayDevice>& hw) {
    const Vector< sp<Layer> >& visibleLayersSortedByZ =
                hw->getVisibleLayersSortedByZ();
    for (size_t i = 0 ; i < visibleLayersSortedByZ.size() ; i++) {
        const sp<Layer>& layer(visibleLayersSortedByZ[i]);
        // LayerDim doesn't have getS3dFormat. This is to avoid RTTI.
        if(strncmp(layer->getTypeId(), "LayerDim", strlen("LayerDim"))) {
            const sp<ExLayer>& exLayer =
                reinterpret_cast<const sp<ExLayer>&>(layer);
            if (exLayer->getS3dFormat(hw) != HWC_S3DMODE_NONE) {
                return true;
            }
        }
    }
    return false;
}
#else
bool ExSurfaceFlinger::isS3DLayerPresent(const sp<const DisplayDevice>&) {
    return false;
}
#endif

#ifdef DEBUG_CONT_DUMPSYS
status_t ExSurfaceFlinger::dump(int fd, const Vector<String16>& args) {
    // Format: adb shell dumpsys SurfaceFlinger --file --no-limit
    size_t numArgs = args.size();
    status_t err = NO_ERROR;

    if (!numArgs || (args[0] != String16("--file"))) {
        return SurfaceFlinger::dump(fd, args);
    }

    Mutex::Autolock _l(mFileDump.lock);

    // Same command is used to start and end dump.
    mFileDump.running = !mFileDump.running;

    if (mFileDump.running) {
        // Create an empty file or erase existing file.
        std::fstream fs;
        fs.open(mFileDump.name, std::ios::out);
        if (!fs) {
            mFileDump.running = false;
            err = UNKNOWN_ERROR;
        } else {
            mFileDump.position = 0;
            if (numArgs >= 2 && (args[1] == String16("--nolimit"))) {
                mFileDump.noLimit = true;
            } else {
                mFileDump.noLimit = false;
            }
        }
    }

    String8 result;
    result += mFileDump.running ? "Start" : "End";
    result += mFileDump.noLimit ? " unlimited" : " fixed limit";
    result += " dumpsys to file : ";
    result += mFileDump.name;
    result += "\n";

    write(fd, result.string(), result.size());

    return NO_ERROR;
}

void ExSurfaceFlinger::dumpDrawCycle(bool prePrepare) {
    Mutex::Autolock _l(mFileDump.lock);

    // User might stop dump collection in middle of prepare & commit.
    // Collect dumpsys again after commit and replace.
    if (!mFileDump.running && !mFileDump.replaceAfterCommit) {
        return;
    }

    Vector<String16> args;
    size_t index = 0;
    String8 dumpsys;

    dumpAllLocked(args, index, dumpsys);

    char timeStamp[32];
    char dataSize[32];
    char hms[32];
    long millis;
    struct timeval tv;
    struct tm *ptm;

    gettimeofday(&tv, NULL);
    ptm = localtime(&tv.tv_sec);
    strftime (hms, sizeof (hms), "%H:%M:%S", ptm);
    millis = tv.tv_usec / 1000;
    snprintf(timeStamp, sizeof(timeStamp), "Timestamp: %s.%03ld", hms, millis);
    snprintf(dataSize, sizeof(dataSize), "Size: %8zu", dumpsys.size());

    std::fstream fs;
    fs.open(mFileDump.name, std::ios::in | std::ios::out);
    if (!fs) {
        ALOGE("Failed to open %s file for dumpsys", mFileDump.name);
        return;
    }

    // Format:
    //    | start code | after commit? | time stamp | dump size | dump data |
    fs.seekp(mFileDump.position, std::ios::beg);

    fs << "#@#@-- DUMPSYS START --@#@#" << std::endl;
    fs << "PostCommit: " << ( prePrepare ? "false" : "true" ) << std::endl;
    fs << timeStamp << std::endl;
    fs << dataSize << std::endl;
    fs << dumpsys << std::endl;

    if (prePrepare) {
        mFileDump.replaceAfterCommit = true;
    } else {
        mFileDump.replaceAfterCommit = false;
        // Reposition only after commit.
        // Keem file size to appx 20 MB limit by default, wrap around if exceeds.
        mFileDump.position = fs.tellp();
        if (!mFileDump.noLimit && (mFileDump.position > (20 * 1024 * 1024))) {
            mFileDump.position = 0;
        }
    }

    fs.close();
}
#endif

}; // namespace android
