<?PHP
#
#   FILE:  PopupWindow.php
#
#   Part of the ScoutLib application support library
#   Copyright 2002-2013 Edward Almasy and Internet Scout Research Group
#   http://scout.wisc.edu/
#

/**
* Lightboxed pop-up window with repeat prevention.
*/
class PopupWindow {

    # ---- PUBLIC INTERFACE --------------------------------------------------

    /**
    * Object constructor.
    * @param int $PopupId Integer ID of pop-up window.
    * @param Database $DB Database object to use for storage and retrieval of
    *      window information.
    * @param int $UserId User ID for repeat prevention.  Defaults to NULL, which
    *      indicates no user ID is available.
    */
    function PopupWindow($PopupId, $DB, $UserId = NULL)
    {
        # save our window ID and database handle
        $this->Id = intval($PopupId);
        $this->DB = $DB;
        $this->UserId = $UserId ? intval($UserId) : NULL;

        # set defaults
        $this->Width = 400;
        $this->Height = 200;
        $this->ForceDisplay = FALSE;
        $this->SeenCountThreshold = 5;
        $this->SeenTimeThreshold = 60;
        $this->CookieLifetimeInDays = 90;
    }

    /**
    * Initialize pop-up window tracking. This must be called before any HTML
    * output.
    * @param int $CountThreshold Number of times user must been seen before
    *      window displays.
    * @param int $TimeThreshold Minimum number of seconds from when user was
    *     first seen before window displays.
    */
    function Initialize($CountThreshold, $TimeThreshold)
    {
        $this->SeenCountThreshold = $CountThreshold;
        $this->SeenTimeThreshold = $TimeThreshold;
        $this->ShouldDisplay();
    }

    /**
    * Set the width of the pop-up window. This must be called before any code
    * output method.
    * @param int $NewWidth New pop-up window width setting.
    */
    function Width($NewWidth) {  $this->Width = intval($NewWidth);  }

    /**
    * Set the height of the pop-up window. This must be called before any code
    * output method.
    * @param int $NewHeight New pop-up window height setting.
    */
    function Height($NewHeight) {  $this->Height = intval($NewHeight);  }

    /**
    * Get the integer ID of window.
    * @return ID value.
    */
    function Id() {  return $this->Id;  }

    /**
    * Report whether the pop-up window will display if code printing methods
    * are called.
    * @return Returns TRUE if window will display.
    */
    function WillDisplay() {  return $this->ShouldDisplay();  }

    /**
    * Sets the flag that forces the pop-up window to display.
    * @param bool $Display TRUE to force window to display.
    */
    function AlwaysDisplay($Display) {  $this->ForceDisplay = $Display;  }

    /**
    * Print header code if appropriate. The header code includes HTML tags for
    * the necessary CSS and JavaScript files
    */
    function PrintHeaderCode()
    {
        # if we should display the window
        if ($this->ShouldDisplay())
        {
            ?>
            <style type="text/css">@import 'include/thickbox.css';</style>
            <script type="text/javascript" src="include/SPT--jQuery.js"></script>
            <script type="text/javascript" src="include/thickbox-compressed.js"></script>
            <script type="text/javascript">
                $(document).ready(function(){
                    tb_show('', '#TB_inline?inlineId=PopupWindowContent<?PHP
                            print($this->Id());  ?>&width=<?PHP
                            print($this->Width);  ?>&height=<?PHP
                            print($this->Height);  ?>&modal=true', 'null');
                });
            </script>
            <?PHP
        }
    }

    /**
    * Print the beginning code wrapper for the pop-up window content section in
    * page body if appropriate.
    */
    function PrintBeginContentCode()
    {
        # if we should display the window
        if ($this->ShouldDisplay())
        {
            # display code for beginning of content section
            ?><div id="PopupWindowContent<?PHP  print($this->Id());
                    ?>" style="display: none;"><span><?PHP
        }
    }

    /**
    * Print the ending code wrapper for the pop-up window content section in
    * page body if appropriate.
    */
    function PrintEndContentCode()
    {
        # if we should display the window
        if ($this->ShouldDisplay())
        {
            # display code for end of content section
            ?></span></div><?PHP
        }
    }


    # ---- PRIVATE INTERFACE -------------------------------------------------

    private $Id;
    private $DB;
    private $UserId;
    private $Width;
    private $Height;
    private $ForceDisplay;
    private $SeenCountThreshold;
    private $SeenTimeThreshold;
    private $CookieLifetimeInDays;
    private $DisplayStatus;             # local to ShouldDisplay()
    private $UserIdSeenCount;           # local to SeenCountForUserId()
    private $UserIdFirstSeen;
    private $IPAddressSeenCount;        # local to SeenCountForIPAddress()
    private $IPAddressFirstSeen;

    /**
    * Determine whether the pop-up window should be displayed. Whether the
    * pop-up window is displayed depends on a number of factors, including
    * whether display should be forced, whether the user has seen it more times
    * than the threshold, etc.
    * @return Returns TRUE if the pop-up window should be displayed.
    */
    private function ShouldDisplay()
    {
        # if user requested always display return TRUE to caller
        if ($this->ForceDisplay) {  return TRUE;  }

        # if we have already determined status for this window
        if (isset($this->DisplayStatus))
        {
            # return status to caller
            return $this->DisplayStatus;
        }

        # if cookie is available
        if (isset($_COOKIE["ScoutPopupCount".$this->Id])
                && isset($_COOKIE["ScoutPopupFirstSeen".$this->Id]))
        {
            # if cookie seen count is below threshold
            $Count = $_COOKIE["ScoutPopupCount".$this->Id];
            if ($Count < $this->SeenCountThreshold)
            {
                # increase cookie seen count
                setcookie("ScoutPopupCount".$this->Id, ($Count + 1),
                        (time() + (60*60*24 * $this->CookieLifetimeInDays)));
                setcookie("ScoutPopupFirstSeen".$this->Id,
                        $_COOKIE["ScoutPopupFirstSeen".$this->Id],
                        (time() + (60*60*24 * $this->CookieLifetimeInDays)));
            }
            else
            {
                # if enough time has elapsed and we are not sure about displaying window
                if ((time() - $_COOKIE["ScoutPopupFirstSeen".$this->Id])
                            >= $this->SeenTimeThreshold)
                {
                    # if cookie seen count is at threshold
                    if ($Count == $this->SeenCountThreshold)
                    {
                        # display the window
                        $Display = TRUE;

                        # increase cookie seen count
                        setcookie("ScoutPopupCount".$this->Id, ($Count + 1),
                                (time() + (60*60*24 * $this->CookieLifetimeInDays)));
                        setcookie("ScoutPopupFirstSeen".$this->Id,
                                $_COOKIE["ScoutPopupFirstSeen".$this->Id],
                                (time() + (60*60*24 * $this->CookieLifetimeInDays)));
                    }
                    else
                    {
                        # do not display the window
                        $Display = FALSE;
                    }
                }
            }
        }
        else
        {
            # set cookie
            setcookie("ScoutPopupFirstSeen".$this->Id, time(),
                    (time() + (60*60*24 * $this->CookieLifetimeInDays)));
            setcookie("ScoutPopupCount".$this->Id, 1,
                    (time() + (60*60*24 * $this->CookieLifetimeInDays)));
        }

        # if we know the user ID
        if ($this->UserId !== NULL)
        {
            # if we have seen this user ID before
            $Count = $this->SeenCountForUserId();
            if ($Count !== NULL)
            {
                # if user ID seen count is below threshold
                if ($Count < $this->SeenCountThreshold)
                {
                    # increase user ID seen count
                    $Count = $this->SeenCountForUserId($Count + 1);
                }
                else
                {
                    # if enough time has elapsed
                    if ($this->SecondsSinceUserIdFirstSeen()
                                    >= $this->SeenTimeThreshold)
                    {
                        # if user ID seen count is at threshold
                        if ($Count == $this->SeenCountThreshold)
                        {
                            # display the window (if not previously disallowed)
                            if (!isset($Display)) {  $Display = TRUE;  }

                            # increase user ID seen count
                            $Count = $this->SeenCountForUserId($Count + 1);
                        }
                        else
                        {
                            # do not display the window
                            $Display = FALSE;
                        }
                    }
                }
            }
            else
            {
                # add user ID to database
                $Count = $this->SeenCountForUserId(1);
            }
        }

        # if we have seen this IP address before
        $Count = $this->SeenCountForIPAddress();
        if ($Count !== NULL)
        {
            # if IP address seen count is below threshold
            if ($Count < $this->SeenCountThreshold)
            {
                # increase IP address seen count
                $Count = $this->SeenCountForIPAddress($Count + 1);
            }
            else
            {
                # if enough time has elapsed
                if ($this->SecondsSinceIPAddressFirstSeen() >= $this->SeenTimeThreshold)
                {
                    # if IP address seen count is at threshold
                    if ($Count == $this->SeenCountThreshold)
                    {
                        # display the window (if not previously disallowed)
                        if (!isset($Display)) {  $Display = TRUE;  }

                        # increase IP address seen count
                        $Count = $this->SeenCountForIPAddress($Count + 1);
                    }
                    else
                    {
                        # do not display the window
                        $Display = FALSE;
                    }
                }
            }
        }
        else
        {
            # add IP address to database
            $Count = $this->SeenCountForIPAddress(1);
        }

        # if we are still not sure whether to display the window
        if (!isset($Display))
        {
            # do not display the window
            $Display = FALSE;
        }

        # save window display status
        $this->DisplayStatus = $Display;

        # return window display status to caller
        return $Display;
    }

    /**
    * Get or set the number of times the user has seen the pop-up window.
    * @param int $NewSeenCount Number of times the user has seen the pop-up
    *     window.
    * @return Returns the number of times the user has seen the pop-up window or
    *     NULL if not set.
    */
    private function SeenCountForUserId($NewSeenCount = NULL)
    {
        # attempt to retrieve count from database
        if (!isset($this->UserIdSeenCount))
        {
            $this->DB->Query("SELECT SeenCount, FirstSeen FROM PopupLog"
                    ." WHERE PopupId = ".$this->Id
                    ." AND SigOne = ".$this->UserId
                    ." AND SigTwo <= 0");
            if( $this->DB->NumRowsSelected() )
            {
                $Tmp = $this->DB->FetchRow();
                $this->UserIdSeenCount = $Tmp["SeenCount"];
                $this->UserIdFirstSeen = $Tmp["FirstSeen"];
            }
            else
            {
                $this->UserIdSeenCount = NULL;
                $this->UserIdFirstSeen = NULL;
            }
        }
        $Count = $this->UserIdSeenCount;

        # if new count supplied
        if ($NewSeenCount !== NULL)
        {
            # if count is already in database
            if ($Count !== NULL)
            {
                # update count in database
                $this->DB->Query("UPDATE PopupLog SET SeenCount = ".$NewSeenCount
                        ." WHERE PopupId = ".intval($this->Id)
                        ." AND SigOne = ".$this->UserId
                        ." AND SigTwo <= 0");
            }
            else
            {
                # add count to database
                $this->DB->Query("INSERT INTO PopupLog"
                        ." (PopupId, SigOne, SigTwo, FirstSeen, SeenCount) VALUES "
                        ." (".$this->Id.", ".$this->UserId.", -1, NOW(), "
                                .$NewSeenCount.")");
            }

            # set current count to new count
            $Count = $NewSeenCount;
        }

        # return current count to caller
        return $Count;
    }


    /**
    * Get or set the number of times any users on the IP address of the current
    * user have seen the pop-up window.
    * @param int $NewSeenCount Number of times any users on the IP address of
    *     the current user have seen the pop-up window.
    * @return Returns the number of times any users on the IP address of the
    *    current user have seen the pop-up window or NULL if not set.
    */
    private function SeenCountForIPAddress($NewSeenCount = NULL)
    {
        # attempt to retrieve count from database
        if (!isset($this->IPAddressSeenCount))
        {
            $this->DB->Query("SELECT SeenCount, FirstSeen FROM PopupLog"
                    ." WHERE PopupId = ".$this->Id
                    ." AND SigOne = ".$this->UserIPSigOne()
                    ." AND SigTwo = ".$this->UserIPSigTwo());
            if( $this->DB->NumRowsSelected() )
            {
                 $Tmp = $this->DB->FetchRow();
                 $this->IPAddressSeenCount = $Tmp["SeenCount"];
                 $this->IPAddressFirstSeen = $Tmp["FirstSeen"];
            }
            else
            {
                 $this->IPAddressSeenCount = NULL;
                 $this->IPAddressFirstSeen = NULL;
            }
        }
        $Count = $this->IPAddressSeenCount;

        # if new count supplied
        if ($NewSeenCount !== NULL)
        {
            # if count is already in database
            if ($Count !== NULL)
            {
                # update count in database
                $this->DB->Query("UPDATE PopupLog SET SeenCount = ".$NewSeenCount
                        ." WHERE SigOne = ".$this->UserIPSigOne()
                        ." AND SigTwo = ".$this->UserIPSigTwo());
            }
            else
            {
                # add count to database
                $this->DB->Query("INSERT INTO PopupLog"
                        ." (PopupId, SigOne, SigTwo, SeenCount, FirstSeen)"
                        ." VALUES (".$this->Id.", "
                                .$this->UserIPSigOne().", "
                                .$this->UserIPSigTwo().", "
                                .$NewSeenCount.", NOW())");
            }

            # set current count to new count
            $Count = $NewSeenCount;
        }

        # return current count to caller
        return $Count;
    }

    /**
    * Get the number of seconds since the current user ID was first seen.
    * @return Returns the number of seconds since the current user ID was first
    *    seen.
    */
    private function SecondsSinceUserIdFirstSeen()
    {
        if (!isset($this->UserIdFirstSeen)) {  $this->SeenCountForUserId();  }
        return time() - strtotime($this->UserIdFirstSeen);
    }

    /**
    * Get the number of seconds since the IP address of the current user was
    * first seen.
    * @return Returns the number of seconds since the IP address of the current
    *    user was first seen.
    */
    private function SecondsSinceIPAddressFirstSeen()
    {
        if (!isset($this->IPAddressFirstSeen)) {  $this->SeenCountForIPAddress();  }
        return time() - strtotime($this->IPAddressFirstSeen);
    }

    /**
    * Get the signature value from the first two bytes of the IP address of the
    * current user.
    * @return Returns the signature value from the first two bytes of the IP
    *     address of the current user.
    */
    private function UserIPSigOne()
    {
        $Bytes = explode(".", $_SERVER["REMOTE_ADDR"]);
        return (count($Bytes) != 4) ? 0
                : (intval($Bytes[0]) * 256) + intval($Bytes[1]);
    }

    /**
    * Get the signature value from the last two bytes of the IP address of the
    * current user.
    * @return Returns the signature value from the last two bytes of the IP
    *     address of the current user.
    */
    private function UserIPSigTwo()
    {
        $Bytes = explode(".", $_SERVER["REMOTE_ADDR"]);
        return (count($Bytes) != 4) ? 0
                : (intval($Bytes[2]) * 256) + intval($Bytes[3]);
    }

}
