/*
 * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Codename One designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *  
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 * 
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 * 
 * Please contact Codename One through http://www.codenameone.com/ if you 
 * need additional information or have any questions.
 */
package com.codename1.impl;

import com.codename1.components.InfiniteProgress;
import com.codename1.io.ConnectionRequest;
import com.codename1.io.NetworkManager;
import com.codename1.ui.Button;
import com.codename1.ui.Command;
import com.codename1.ui.Component;
import com.codename1.ui.Container;
import com.codename1.ui.Dialog;
import com.codename1.ui.Display;
import com.codename1.ui.Form;
import com.codename1.ui.animations.CommonTransitions;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.layouts.GridLayout;
import com.codename1.ui.plaf.UIManager;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Abstract class for fullscreen ads that appear before and possibly after application
 * execution as well as randomly between application screen transitions.
 *
 * @author Shai Almog
 */
public abstract class FullScreenAdService {    
    private boolean allowWithoutNetwork = true;
    private int timeout = 10000;
    private int adDisplayTime = 6000;
    private int timeForNext = -1;
    private boolean scaleMode;
    private boolean allowSkipping;
    private static Object LOCK = new Object();
    
    
    /**
     * Creates a new request for an ad
     * @return the network operation
     */
    protected abstract ConnectionRequest createAdRequest();

    /**
     * Component representing a given ad
     * @return the ad that is currently pending
     */
    protected abstract Component getPendingAd();
    
    /**
     * Just checks if an ad is already fetched
     * @return returns true if an ad is already waiting in the queue
     */
    protected abstract boolean hasPendingAd();
    
    /**
     * Removes the pending ad data so we can fetch a new ad
     */
    protected abstract void clearPendingAd();
    
    /**
     * Returns the URL for the ad
     * @return the ad URL
     */
    protected abstract String getAdDestination();
    
    /**
     * Returns true if the connection failed
     */
    protected abstract boolean failed();
    
    /**
     * Invoked on application startup, this code will download an ad or timeout 
     */
    public void showWelcomeAd() {
        if(!UIManager.getInstance().wasThemeInstalled()) {
            if(Display.getInstance().hasNativeTheme()) {
                Display.getInstance().installNativeTheme();
            }
        }
        ConnectionRequest r = createAdRequest();
        r.setPriority(ConnectionRequest.PRIORITY_HIGH);
        r.setTimeout(timeout);
        InfiniteProgress ip = new InfiniteProgress();
        Dialog ipDialog = ip.showInifiniteBlocking();
        NetworkManager.getInstance().addToQueueAndWait(r);
        if(failed()) {
            ipDialog.dispose();
            if(!allowWithoutNetwork) {
                ipDialog.dispose();
                Dialog.show("Network Error", "Please try again later", "Exit", null);
                Display.getInstance().exitApplication();
            } else {
                return;
            }
        }
        Component c = getPendingAd();
        if(c != null) {
            Form adForm = new AdForm(c);
            adForm.setTransitionInAnimator(CommonTransitions.createEmpty());
            adForm.setTransitionOutAnimator(CommonTransitions.createEmpty());
            adForm.show();
        }
    }
        
    /**
     * Binds an ad to appear periodically after a given timeout
     * @param timeForNext the timeout in which an ad should be shown in milliseconds
     */
    public void bindTransitionAd(final int timeForNext) {
        Runnable onTransitionAndExit = new Runnable() {
            private long lastTime = System.currentTimeMillis();
            public void run() {
                long t = System.currentTimeMillis();
                if(t - lastTime > timeForNext) {
                    lastTime = t;
                    Component c = getPendingAd();
                    if(c != null) {
                        Form adForm = new AdForm(c);
                        adForm.show();
                    }
                }
            }
        };
        CodenameOneImplementation.setOnCurrentFormChange(onTransitionAndExit);
        CodenameOneImplementation.setOnExit(onTransitionAndExit);
        Timer t = new Timer();
        int tm = Math.max(5000, timeForNext - 600);
        t.schedule(new TimerTask() {
            public void run() {
                if(!hasPendingAd()) {
                    ConnectionRequest r = createAdRequest();
                    r.setPriority(ConnectionRequest.PRIORITY_LOW);
                    r.setTimeout(timeout);
                    NetworkManager.getInstance().addToQueue(r);
                }
            }
        }, tm, tm);
    }

    /**
     * If set to true this flag allows the application to load even if an Ad cannot be displayed
     * @return the allowWithoutNetwork
     */
    public boolean isAllowWithoutNetwork() {
        return allowWithoutNetwork;
    }

    /**
     * If set to true this flag allows the application to load even if an Ad cannot be displayed
     * @param allowWithoutNetwork the allowWithoutNetwork to set
     */
    public void setAllowWithoutNetwork(boolean allowWithoutNetwork) {
        this.allowWithoutNetwork = allowWithoutNetwork;
    }

    /**
     * The timeout in milliseconds for an ad request
     * @return the timeout
     */
    public int getTimeout() {
        return timeout;
    }

    /**
     * The timeout in milliseconds for an ad request
     * @param timeout the timeout to set
     */
    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    /**
     * @return the adDisplayTime
     */
    public int getAdDisplayTime() {
        return adDisplayTime;
    }

    /**
     * @param adDisplayTime the adDisplayTime to set
     */
    public void setAdDisplayTime(int adDisplayTime) {
        this.adDisplayTime = adDisplayTime;
    }

    /**
     * @return the scaleMode
     */
    public boolean isScaleMode() {
        return scaleMode;
    }

    /**
     * @param scaleMode the scaleMode to set
     */
    public void setScaleMode(boolean scaleMode) {
        this.scaleMode = scaleMode;
    }

    /**
     * @return the allowSkipping
     */
    public boolean isAllowSkipping() {
        return allowSkipping;
    }

    /**
     * @param allowSkipping the allowSkipping to set
     */
    public void setAllowSkipping(boolean allowSkipping) {
        this.allowSkipping = allowSkipping;
    }
    
    class AdForm extends Form {
        boolean blocked = true;
        private long shown = -1;
        public AdForm(Component ad) {
            BorderLayout bl = new BorderLayout();
            setLayout(bl);
            if(!isScaleMode()) {
                bl.setCenterBehavior(BorderLayout.CENTER_BEHAVIOR_CENTER);
            } 
            addComponent(BorderLayout.CENTER, ad);                
            Command open = new Command("Open") {
                public void actionPerformed(ActionEvent ev) {
                    synchronized(LOCK) {
                        blocked = false;
                        LOCK.notify();
                    }
                    
                    // move to the next screen so the ad will be shown and so we 
                    // can return to the next screen and not this screen
                    Display.getInstance().callSerially(new Runnable() {
                        public void run() {
                            // prevent a potential race condition with the locking
                            if(Display.getInstance().getCurrent() instanceof AdForm) {
                                Display.getInstance().callSerially(this);
                                return;
                            }
                            Display.getInstance().execute(getAdDestination());
                        }
                    });
                }
            };
            Command skip = new Command("Skip") {
                public void actionPerformed(ActionEvent ev) {
                    synchronized(LOCK) {
                        blocked = false;
                        LOCK.notify();
                    }
                }
            };
            if(Display.getInstance().isTouchScreenDevice()) {
                Container grid = new Container(new GridLayout(1, 2));
                grid.addComponent(new Button(open));
                if(isAllowSkipping()) {
                    grid.addComponent(new Button(skip));
                }
                addComponent(BorderLayout.SOUTH, grid);
            } else {
                addCommand(open);
                if(isAllowSkipping()) {
                    addCommand(skip);
                }
            }
            registerAnimated(this);
        }
        
        protected void onShow() {
            shown = System.currentTimeMillis();
        }

        public void show() {
            super.showBack();
            Display.getInstance().invokeAndBlock(new Runnable() {
                public void run() {
                    try {
                        synchronized(LOCK) {
                            while(blocked) {
                                LOCK.wait(100);
                            }
                        }
                    } catch(Exception err) {
                        err.printStackTrace();
                    }
                }
            });
            clearPendingAd();
        }
        
        public boolean animate() {
            if(shown > -1 && System.currentTimeMillis() - shown >= adDisplayTime) {
                blocked = false;
            }
            return false;
        }
    }
}
