text:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

using Windows.UI.Popups;
using System.ComponentModel;

using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.ApplicationModel;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Activation;
using Windows.Storage;
using System.Threading.Tasks;
using Windows.Security.Cryptography;
using Windows.UI.Xaml.Media.Animation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace minesweeper
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        /// <summary>
        /// 服务端 socket
        /// </summary>
        private StreamSocketListener _listener;

        /// <summary>
        /// 客户端 socket
        /// </summary>
        private StreamSocket _client;

        /// <summary>
        /// 客户端向服务端发送数据时的 DataWriter
        /// </summary>
        private DataWriter _writer;

        /// <summary>
        /// enum type of possible statuses of the state machine
        /// </summary>
        enum Status
        {
            LocalWaiting,
            LocalPlaying,
            NetListening,
            NetConnected,
            NetReady,
            NetStarting,
            NetPlaying
        };

        /// <summary>
        /// the actual status variable
        /// </summary>
        private Status _gameStatus;

        /// <summary>
        /// property to access status
        /// used to hide and show the second progressBar
        /// </summary>
        private Status gameStatus
        {
            get
            {
                return _gameStatus;
            }
            set
            {
                if (_gameStatus.ToString().StartsWith("Local") && value.ToString().StartsWith("Net"))
                {
                    componentMarkedProgressBar.Visibility = Windows.UI.Xaml.Visibility.Visible;
                    componentRevealedProgressBar.Visibility = Windows.UI.Xaml.Visibility.Visible;
                    componentTextBlock.Visibility = Windows.UI.Xaml.Visibility.Visible;
                }
                else if (_gameStatus.ToString().StartsWith("Local") && value.ToString().StartsWith("Net"))
                {
                    componentMarkedProgressBar.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                    componentRevealedProgressBar.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                    componentTextBlock.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                }
                _gameStatus = value;
            }
        }

        /// <summary>
        /// enum type of possible difficulties of the game
        /// </summary>
        enum Difficulty
        {
            Low,
            Intermediate,
            High
        };

        /// <summary>
        /// current difficulty of the game
        /// </summary>
        private Difficulty gameDifficulty;

        /// <summary>
        /// strings corresponding to different diffculties
        /// used to compose highscore message
        /// </summary>
        private readonly string[] difficultyString = { "低级", "中级", "高级" };

        /// <summary>
        /// the board that contains all zones
        /// implemented in a separating class
        /// </summary>
        private Board board;

        /// <summary>
        /// timers for countdown and update elapsed time string
        /// both used in online playing
        /// </summary>
        private DispatcherTimer countdownTimer, gameElapsedTimer;

        /// <summary>
        /// time remaining before the game starts
        /// used for online playing to let both players get ready
        /// </summary>
        private int _countDown;

        /// <summary>
        /// status used for online playing
        /// -1 means failed
        /// 0 means normal
        /// 1 means done
        /// </summary>
        private int finished;

        /// <summary>
        /// the start time of this game(not application)
        /// </summary>
        private DateTime startTime;

        /// <summary>
        /// total time used in this game
        /// caculated from startTime
        /// </summary>
        private TimeSpan elapsedTime;

        /// <summary>
        /// the filename to store persistent data
        /// the data is store in local app storage
        /// note that rebuilding the app ruins it
        /// </summary>
        const string DatafileName = "high score.dat";

        /// <summary>
        /// the file object of the datafile
        /// provides read and write operations
        /// </summary>
        private StorageFile dataFile;

        /// <summary>
        /// pure colour brushes
        /// used to display current difficulty
        /// each difficulty has two states, light and dark
        /// 3 * 2 = 6 brushes in total
        /// </summary>
        static private Brush
            lowDarkBrush
          , lowLightBrush
          , intermediateDarkBrush
          , intermediateLightBrush
          , highDarkBrush
          , highLightBrush
        ;

        /// <summary>
        /// the highest score of low, intermediate, high diffculty, respectively
        /// -1 represents that there is not yet a score
        /// reads from storage at start of the application
        /// writes back when the application is closed
        /// </summary>
        private int[] highscores;

        /// <summary>
        /// initializes the UI, some necessary setup
        /// </summary>
        public MainPage()
        {
            this.InitializeComponent();
            board = new Board();
            gameStatus = Status.LocalWaiting;
            gameDifficulty = Difficulty.Low;
            changeSize(Board.size.small);
            refreshBoardView();

            // set data binding
            minesLeft.DataContext = board;
            selfRevealedProgressBar.DataContext = board;
            selfMarkedProgressBar.DataContext = board;
            componentRevealedProgressBar.DataContext = board;
            componentMarkedProgressBar.DataContext = board;

            // add win and lose delegate
            board.win += winHandler;
            board.lose += loseHandler;

            // initialize countDown_dispatcherTimer
            countdownTimer = new DispatcherTimer();
            countdownTimer.Interval = new TimeSpan(0, 0, 0, 1);
            countdownTimer.Tick += countdownTick;
            gameElapsedTimer = new DispatcherTimer();
            gameElapsedTimer.Interval = new TimeSpan(0, 0, 0, 0, 200);
            gameElapsedTimer.Tick += gameElapsedTick;

            // set for share
            DataTransferManager.GetForCurrentView().DataRequested += MainPage_DataRequested;

            // initialize brush for difficuly button
            lowDarkBrush = new SolidColorBrush(Windows.UI.Colors.DarkGreen);
            lowLightBrush = new SolidColorBrush(Windows.UI.Colors.GreenYellow);
            intermediateDarkBrush = new SolidColorBrush(Windows.UI.Color.FromArgb(0xFF, 0xCE, 0xB2, 0x00));
            intermediateLightBrush = new SolidColorBrush(Windows.UI.Colors.Yellow);
            highDarkBrush = new SolidColorBrush(Windows.UI.Colors.DarkRed);
            highLightBrush = new SolidColorBrush(Windows.UI.Colors.Tomato);

            readFromDataFile();

            // hack
            board.PropertyChanged += refreshProgressBar;
        }

        /// <summary>
        /// reads data from datafile
        /// </summary>
        private async void readFromDataFile()
        {
            // setup local data
            StorageFolder localFolder = ApplicationData.Current.LocalFolder;
            bool fileExists = true;
            try
            {
                dataFile = await localFolder.GetFileAsync(DatafileName);
            }
            catch
            {
                fileExists = false;
            }
            if (!fileExists)
            {
                dataFile = await localFolder.CreateFileAsync(DatafileName);
                highscores = new int[] { -1, -1, -1 };
                await FileIO.WriteTextAsync(dataFile, getBase64HighScoreString());
            }
            else
            {
                // use Base64 to prevent simple hack
                string strIn = await FileIO.ReadTextAsync(dataFile);
                IBuffer buffFromBase64;
                try
                {
                    buffFromBase64 = CryptographicBuffer.DecodeFromBase64String(strIn);
                }
                catch
                {
                    messageBox("数据文件损坏,最高分纪录将被重置!");
                    highscores = new int[] { -1, -1, -1 };
                    writeToDataFile();
                    return;  // if I use a bool instead, VS will think buffFromBase64 is used uninitialized...
                }
                String highscoreString = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, buffFromBase64);
                string[] highscoreStrings = highscoreString.Split(new char[] { '|' });
                if (highscoreStrings.Count() != 3)
                {
                    messageBox("数据文件损坏,最高分纪录将被重置!");
                    highscores = new int[] { -1, -1, -1 };
                    await FileIO.WriteTextAsync(dataFile, getBase64HighScoreString());
                }
                else
                {
                    highscores = new int[] {
                        int.Parse(highscoreStrings[0]),
                        int.Parse(highscoreStrings[1]),
                        int.Parse(highscoreStrings[2])
                    };
                }
            }
        }

        /// <summary>
        /// constructs the highscoreString and encodes it with Base64
        /// </summary>
        /// <returns>the Base64 string</returns>
        private string getBase64HighScoreString()
        {
            string originHighScoreString = highscores[0].ToString()
                                            + "|" + highscores[1].ToString()
                                            + "|" + highscores[2].ToString();
            IBuffer buffUTF8 = CryptographicBuffer.ConvertStringToBinary(originHighScoreString, BinaryStringEncoding.Utf8);
            return CryptographicBuffer.EncodeToBase64String(buffUTF8);
        }

        /// <summary>
        /// saves the highscoreString in the datafile
        /// </summary>
        private async void writeToDataFile()
        {
            await FileIO.WriteTextAsync(dataFile, getBase64HighScoreString());
        }

        /// <summary>
        /// refreshes the whole board
        /// used when the game is reset or the difficulty is changed
        /// </summary>
        private void refreshBoardView()
        {
            // must add 1 in Windows 10, otherwise one column will disappear
            boardView.Width = board.actualBoardSize[(int)board.currentBoardSize].y * 36 + 1;
            boardView.Height = board.actualBoardSize[(int)board.currentBoardSize].x * 36;
            boardView.ItemsSource = board.zoneList;
        }

        /// <summary>
        /// starts recording game elapsed time
        /// resets the displayed elapsed time
        /// </summary>
        private void gameStart()
        {
            startTime = DateTime.Now;
            timeElaspedTextBlock.Text = "0";
            gameElapsedTimer.Start();
        }

        /// <summary>
        /// game finished
        /// records game elapsed time
        /// save new highscore if neccessary
        /// </summary>
        private void gameOver()
        {
            elapsedTime = DateTime.Now.Subtract(startTime);
            gameElapsedTimer.Stop();
            int elapsedTimeInMillisecond = (int)elapsedTime.TotalMilliseconds;
            // new highscore
            if (highscores[(int)gameDifficulty] == -1 || elapsedTimeInMillisecond < highscores[(int)gameDifficulty])
            {
                messageBox(
                    "Congratulations! You achieved a new highscore of "
                    + convertMillisecondsToSecondsString(elapsedTimeInMillisecond)
                    + "s"
                );
                highscores[(int)gameDifficulty] = elapsedTimeInMillisecond;
                writeToDataFile();
            }
        }

        /// <summary>
        /// handle the event that a zone is clicked
        /// </summary>
        /// <param name="sender">the zone clicked by the user</param>
        /// <param name="e"></param>
        private void Zone_Click(object sender, RoutedEventArgs e)
        {
            // ####
            Button button = sender as Button;
            writeLog(button.Tag as string);
            if (gameStatus == Status.LocalWaiting && board.currentBoardState == Board.state.playing)
            {
                gameStart();
                gameStatus = Status.LocalPlaying;
            }
            if (gameStatus == Status.LocalPlaying || gameStatus == Status.NetPlaying)
            {
                // Button button = sender as Button;
                board.open(int.Parse(button.Tag as string));
            }
            if (gameStatus == Status.NetPlaying && finished == 0)
                sendBoardStatistic();
        }

        /// <summary>
        /// mark a bomb
        /// </summary>
        /// <param name="sender">the button right clicked by the user</param>
        /// <param name="e"></param>
        private void Zone_Right_Click(object sender, RoutedEventArgs e)
        {
            if (gameStatus == Status.LocalWaiting || gameStatus == Status.LocalPlaying || gameStatus == Status.NetPlaying)
            {
                Button button = sender as Button;
                board.mark(int.Parse(button.Tag as string));
            }
            if (gameStatus == Status.NetPlaying)
                sendBoardStatistic();
        }

        /// <summary>
        /// handles the event that a zone is double clicked
        /// </summary>
        /// <param name="sender">the zone double clicked by the user</param>
        /// <param name="e"></param>
        private void Zone_Double_Click(object sender, RoutedEventArgs e)
        {
            if (gameStatus == Status.LocalPlaying || gameStatus == Status.NetPlaying)
            {
                Button button = sender as Button;
                board.explore(int.Parse(button.Tag as string));
            }
            if (gameStatus == Status.NetPlaying && finished == 0)
                sendBoardStatistic();
        }

        /// <summary>
        /// start a new game
        /// </summary>
        /// <param name="sender">the replay button</param>
        /// <param name="e"></param>
        private void replay(object sender, RoutedEventArgs e)
        {
            if (gameStatus == Status.LocalWaiting || gameStatus == Status.LocalPlaying)
            {
                board.reset();
                refreshBoardView();
                gameStatus = Status.LocalWaiting;
                gameElapsedTimer.Stop();
            }
            else
            {
                writeLog("只有在单机模式下才可以replay,请先结束对战");
            }
        }

        /// <summary>
        /// try to change the size of the board
        /// </summary>
        /// <param name="value">the new size</param>
        /// <returns>succeed or not</returns>
        private bool changeSize(Board.size value)
        {
            if (gameStatus == Status.LocalWaiting || gameStatus == Status.LocalPlaying || gameStatus == Status.NetListening || gameStatus == Status.NetConnected)
            {
                if (gameStatus == Status.LocalPlaying)
                    gameElapsedTimer.Stop();
                board.setBoardSize(value);
                refreshBoardView();
                if (gameStatus.ToString().StartsWith("Local"))
                    gameStatus = Status.LocalWaiting;
                return true;
            }
            else
            {
                writeLog("对战过程中不可以设定大小");
                return false;
            }
        }

        /// <summary>
        /// hack
        /// forces the progressBar to refresh
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void refreshProgressBar(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "numRevealedSafe")
                // hack the weird bug when switching from bigger map to small map
                selfRevealedProgressBar.Value = board.numRevealedSafe;
        }

        /// <summary>
        /// changes the difficulty to low
        /// </summary>
        /// <param name="sender">the low buton</param>
        /// <param name="e"></param>
        private void low(object sender, RoutedEventArgs e)
        {
            if (gameDifficulty != Difficulty.Low && changeSize(Board.size.small))
            {
                gameDifficulty = Difficulty.Low;
                lowButtonFill.Fill = lowLightBrush;
                intermediateButtonFill.Fill = intermediateDarkBrush;
                highButtonFill.Fill = highDarkBrush;
            }
        }

        /// <summary>
        /// changes the difficulty to intermediate
        /// </summary>
        /// <param name="sender">the intermediate button</param>
        /// <param name="e"></param>
        private void intermediate(object sender, RoutedEventArgs e)
        {
            if (gameDifficulty != Difficulty.Intermediate && changeSize(Board.size.medium))
            {
                gameDifficulty = Difficulty.Intermediate;
                lowButtonFill.Fill = lowDarkBrush;
                intermediateButtonFill.Fill = intermediateLightBrush;
                highButtonFill.Fill = highDarkBrush;
            }
        }

        /// <summary>
        /// changes the difficulty to high
        /// </summary>
        /// <param name="sender">the high button</param>
        /// <param name="e"></param>
        private void high(object sender, RoutedEventArgs e)
        {
            if (gameDifficulty != Difficulty.High && changeSize(Board.size.big))
            {
                gameDifficulty = Difficulty.High;
                lowButtonFill.Fill = lowDarkBrush;
                intermediateButtonFill.Fill = intermediateDarkBrush;
                highButtonFill.Fill = highLightBrush;
            }
        }

        /// <summary>
        /// used by the board to nofity the mainPage that the game is done
        /// </summary>
        private void winHandler()
        {
            if (gameStatus == Status.LocalPlaying)
            {
                gameStatus = Status.LocalWaiting;
                gameOver();
                messageBox("You win in " + convertMillisecondsToSecondsString((int)elapsedTime.TotalMilliseconds) + "s!");
            }
            else
            {
                finished = 1;
                gameOver();
                sendBySocket("done " + ((int)elapsedTime.TotalMilliseconds).ToString());
            }
        }

        /// <summary>
        /// used by the board to nofity the mainPage that the game is failed
        /// </summary>
        private void loseHandler()
        {
            gameElapsedTimer.Stop();
            if (gameStatus == Status.LocalPlaying)
            {
                gameStatus = Status.LocalWaiting;
                messageBox("You lose!");
            }
            // lose when dueling
            else
            {
                elapsedTime = DateTime.Now.Subtract(startTime);
                finished = -1;
                sendBySocket("fail " + ((int)elapsedTime.TotalMilliseconds).ToString());
            }
        }

        /// <summary>
        /// a simple utility function to pop a messageBox
        /// user will have to confirm before continuing
        /// should be used for important message
        /// </summary>
        /// <param name="message">message to be shown</param>
        private async void messageBox(string message)
        {
            // may be call simultanous and throw an exception
            try
            {
                MessageDialog hint = new MessageDialog(message);
                await hint.ShowAsync();
            }
            catch
            {
                // do nothing
            }
        }

        /// <summary>
        /// a simple utility function to output log message
        /// message will be displayed in a box
        /// the difference from messageBox is that it's not offensive
        /// should be used for unimportant message
        /// </summary>
        /// <param name="message">message to be shown</param>
        private void writeLog(string message)
        {
            log.Text += message;
            log.Text += Environment.NewLine;

            // log.Content += message + Environment.NewLine;
            // log.UpdateLayout();
            // log.ScrollToVerticalOffset(log.ScrollableHeight);
        }

        /// <summary>
        /// handler of the start listen button
        /// </summary>
        /// <param name="sender">the start listen button</param>
        /// <param name="e"></param>
        private void startListen(object sender, RoutedEventArgs e)
        {
            if (gameStatus == Status.LocalWaiting || gameStatus == Status.LocalPlaying)
                _startListen();
        }

        /// <summary>
        /// 在服务端启动一个 socket 监听
        /// </summary>
        private async void _startListen()
        {
            // 实例化一个 socket 监听对象
            _listener = new StreamSocketListener();
            // 监听在接收到一个连接后所触发的事件
            _listener.ConnectionReceived += _listener_ConnectionReceived;

            try
            {
                // 在指定的端口上启动 socket 监听
                // await _listener.BindServiceNameAsync("2211");
                await _listener.BindEndpointAsync(new HostName("192.168.1.101"), "2211");

                writeLog("已经在本机的 2211 端口启动了 socket(tcp) 监听");
                gameStatus = Status.NetListening;
            }
            catch (Exception ex)
            {
                SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);

                writeLog("errStatus: " + errStatus.ToString());
                writeLog(ex.ToString());
            }
        }

        /// <summary>
        /// socket 监听接收到一个连接后
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        async void _listener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
        {
            if (gameStatus == Status.NetListening)
            {
                // 实例化一个 DataReader,用于读取数据
                DataReader reader = new DataReader(args.Socket.InputStream);

                await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    writeLog("服务端收到了来自: " + args.Socket.Information.RemoteHostName.RawName + ":" + args.Socket.Information.RemotePort + " 的 socket 连接");
                });

                // 记住client
                _client = args.Socket;

                // set status
                gameStatus = Status.NetConnected;

                try
                {
                    while (true)
                    {
                        // 自定义协议(header|body):前4个字节代表实际数据的长度,之后的实际数据为一个字符串数据

                        // 读取 header
                        uint sizeFieldCount = await reader.LoadAsync(sizeof(uint));
                        if (sizeFieldCount != sizeof(uint))
                        {
                            // 在获取到合法数据之前,socket 关闭了
                            return;
                        }

                        // 读取 body
                        uint stringLength = reader.ReadUInt32();
                        uint actualStringLength = await reader.LoadAsync(stringLength);
                        if (stringLength != actualStringLength)
                        {
                            // 在获取到合法数据之前,socket 关闭了
                            return;
                        }

                        await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                        () =>
                        {
                            // 显示客户端发送过来的数据
                            string receivedData = reader.ReadString(actualStringLength);
                            // writeLog("接收到数据: " + receivedData);
                            messageHandler(receivedData);
                        });
                    }
                }
                catch (Exception ex)
                {
                    var ignore = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                    {
                        SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);

                        writeLog("errStatus: " + errStatus.ToString());
                        writeLog(ex.ToString());
                    });
                }
            }
        }

        /// <summary>
        /// process the data received from network
        /// take appropriate action accroding to the message and status
        /// </summary>
        /// <param name="receivedData"></param>
        private void messageHandler(string receivedData)
        {
            if (receivedData == "Bye")
            {
                writeLog("对手主动结束,游戏结束");
                netEndGame(this, new RoutedEventArgs());
                return;
            }
            // 收到准备信息
            else if (receivedData == "Ready" || receivedData.Length >= 81)
            {
                if (gameStatus == Status.NetConnected)
                    gameStatus = Status.NetReady;
                else
                    startCountDown();
                if (_listener == null || _listener.Information.LocalPort != "2211")
                {
                    board.FromString(receivedData);
                    refreshBoardView();
                    writeLog("已经收到地图,游戏可以开始...");
                }
            }
            // 游戏结束,对手结束了或者对面知道了
            else
            {
                string[] result = receivedData.Split(new char[] { ' ' });
                int ms = int.Parse(result[1]);
                TimeSpan ComponentTime = new TimeSpan(0, 0, ms / 1000 / 60, ms / 1000 % 60, ms % 1000);
                // 对手完成了
                if (result[0] == "done")
                {
                    writeLog("对手完成了,用时 " + ComponentTime.ToString());
                    // 这边更快
                    if (finished == 1 && elapsedTime.CompareTo(ComponentTime) < 0)
                    {
                        messageBox("You win!");
                    }
                    else
                    {
                        messageBox("You lose!");
                    }
                }
                // 对手扑街了
                else if (result[0] == "fail")
                {
                    writeLog("对手扑街了,用时 " + ComponentTime.ToString());
                    // 这边更快
                    if (finished == -1 && elapsedTime.CompareTo(ComponentTime) < 0)
                    {
                        messageBox("You lose!");
                    }
                    else
                    {
                        messageBox("You win!");
                    }
                }
                // 对手确认
                else if (result[0] == "gotit")
                {
                    if (finished == -1)
                    {
                        messageBox("You lose!");
                    }
                    else
                    {
                        messageBox("You win!");
                    }
                }
                // 对手状态更新
                else
                {
                    componentRevealedProgressBar.Value = int.Parse(result[0]);
                    componentMarkedProgressBar.Value = int.Parse(result[1]);
                }
                // 游戏结束
                if (result[0] == "done" || result[0] == "fail" || result[0] == "gotit")
                {
                    if (finished == 0)
                        sendBySocket("gotit 0");
                    gameElapsedTimer.Stop();
                    gameStatus = Status.NetConnected;
                }
            }
        }

        /// <summary>
        /// 开始游戏前的倒计时
        /// </summary>
        private void startCountDown()
        {
            finished = 0;
            gameStatus = Status.NetStarting;
            _countDown = 3;
            countDownTextBlock.Text = _countDown.ToString();
            countDownTextBlock.Visibility = Windows.UI.Xaml.Visibility.Visible;
            countdownTimer.Start();
        }

        /// <summary>
        /// handler for a tick of the countdownTimer
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void countdownTick(object sender, object e)
        {
            writeLog("tick");
            --_countDown;
            if (_countDown == 0)
            {
                countdownTimer.Stop();
                countDownTextBlock.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
                if (gameStatus == Status.NetStarting)
                {
                    gameStatus = Status.NetPlaying;
                    gameStart();
                }
            }
            else
            {
                countDownTextBlock.Text = _countDown.ToString();
            }
        }

        /// <summary>
        /// handler for tick of the gameElapsedTimer
        /// updates the timeElaspedTextBlock
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void gameElapsedTick(object sender, object e)
        {
            TimeSpan timeElapsedByNow = DateTime.Now.Subtract(startTime);
            timeElaspedTextBlock.Text = ((int)timeElapsedByNow.TotalSeconds).ToString();
        }

        /// <summary>
        /// 创建一个客户端 socket,并连接到服务端 socket
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void connectToServer(object sender, RoutedEventArgs e)
        {
            if (gameStatus == Status.LocalWaiting || gameStatus == Status.LocalPlaying)
            {
                // debug
                writeLog("connecting to " + serverIp.Text);
                HostName hostName;
                try
                {
                    hostName = new HostName(serverIp.Text);
                    // hostName = new HostName("127.0.0.1");
                }
                catch (Exception ex)
                {
                    writeLog(ex.ToString());
                    return;
                }

                // 实例化一个客户端 socket 对象
                _client = new StreamSocket();

                try
                {
                    // 连接到指定的服务端 socket
                    await _client.ConnectAsync(hostName, "2211");

                    gameStatus = Status.NetConnected;

                    writeLog("已经连接上了 " + serverIp.Text + ":2211");
                    // 实例化一个 DataReader,用于读取数据
                    DataReader reader = new DataReader(_client.InputStream);

                    try
                    {
                        while (true)
                        {
                            // 自定义协议(header|body):前4个字节代表实际数据的长度,之后的实际数据为一个字符串数据

                            // 读取 header
                            uint sizeFieldCount = await reader.LoadAsync(sizeof(uint));
                            if (sizeFieldCount != sizeof(uint))
                            {
                                // 在获取到合法数据之前,socket 关闭了
                                return;
                            }

                            // 读取 body
                            uint stringLength = reader.ReadUInt32();
                            uint actualStringLength = await reader.LoadAsync(stringLength);
                            if (stringLength != actualStringLength)
                            {
                                // 在获取到合法数据之前,socket 关闭了
                                return;
                            }

                            await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
                            () =>
                            {
                                // 显示客户端发送过来的数据
                                string receivedData = reader.ReadString(actualStringLength);
                                // writeLog("接收到数据: " + receivedData);
                                messageHandler(receivedData);
                            });
                        }
                    }
                    catch (Exception ex)
                    {
                        var ignore = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                        {
                            SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);

                            writeLog("errStatus: " + errStatus.ToString());
                            writeLog(ex.ToString());
                        });
                    }
                    //writeLog("已经连接上了 127.0.0.1:2211");
                }
                catch (Exception ex)
                {
                    SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);

                    writeLog("errStatus: " + errStatus.ToString());
                    writeLog(ex.ToString());
                }
            }
        }

        /// <summary>
        /// handler of the start game button
        /// sends out the map if this is server
        /// sends out ready if this is client
        /// starts the game if both player is ready
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void netStartGame(object sender, RoutedEventArgs e)
        {
            if (gameStatus == Status.NetConnected || gameStatus == Status.NetReady)
            {
                // 服务器发地图
                if (_listener != null && _listener.Information.LocalPort == "2211")
                {
                    board.reset();
                    refreshBoardView();
                    writeLog("正在向客户端发送地图...");
                    sendBySocket(board.ToString(), "发送成功,即将开始游戏");
                }
                // 客户端发Ready
                else
                {
                    writeLog("正在发出准备信息...");
                    sendBySocket("Ready", "成功发送准备信息");
                }
                if (gameStatus == Status.NetConnected)
                    gameStatus = Status.NetReady;
                else
                    startCountDown();
            }
        }

        /// <summary>
        /// 发送已翻开的数量和已插旗的数量
        /// 用于board更新时将新状态发送给对方
        /// 信息将被用于更新进度条
        /// 特别地,游戏完成时不会被调用
        /// </summary>
        private void sendBoardStatistic()
        {
            sendBySocket(board.numRevealedSafe.ToString() + " " + board.numMarkedMines.ToString());
        }

        /// <summary>
        /// handler of the disconnect button
        /// closes the socket
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void netEndGame(object sender, RoutedEventArgs e)
        {
            if (gameStatus.ToString().StartsWith("Net"))
            {
                gameStatus = Status.LocalWaiting;
                if (_client != null)
                    sendBySocket("Bye");
                closeSocket();
            }
        }

        /// <summary>
        /// 从客户端 socket 发送一个字符串数据到服务端 socket
        /// </summary>
        /// <param name="message"></param>
        /// <param name="successEcho"></param>
        private async void sendBySocket(string message, string successEcho = null)
        {
            // 实例化一个 DataWriter,用于发送数据
            if (_writer == null)
                _writer = new DataWriter(_client.OutputStream);

            // 自定义协议(header|body):前4个字节代表实际数据的长度,之后的实际数据为一个字符串数据

            _writer.WriteUInt32(_writer.MeasureString(message)); // 写 header 数据
            _writer.WriteString(message); // 写 body 数据

            try
            {
                // 发送数据
                await _writer.StoreAsync();

                if (successEcho != null)
                    writeLog(successEcho);
            }
            catch (Exception ex)
            {
                SocketErrorStatus errStatus = SocketError.GetStatus(ex.HResult);

                writeLog("errStatus: " + errStatus.ToString());
                writeLog(ex.ToString());
            }
        }

        // 关闭客户端 socket 和服务端 socket
        private void closeSocket()
        {
            try
            {
                if (_writer != null)
                {
                    _writer.DetachStream(); // 分离 DataWriter 与 Stream 的关联
                    _writer.Dispose();
                    _writer = null;
                }

                if (_client != null)
                {
                    _client.Dispose();
                    _client = null;
                }
                if (_listener != null)
                {
                    _listener.Dispose();
                    _listener = null;
                }

                writeLog("已经断开连接");
                gameStatus = Status.LocalWaiting;
            }
            catch (Exception ex)
            {
                writeLog(ex.ToString());
            }
        }

        /// <summary>
        /// handler of the help button
        /// displays help message
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void showHelp(object sender, RoutedEventArgs e)
        {
            // todo
            messageBox("左键翻开,右键标雷,双击自动打开。\n"
                + "网络对战时,由其中一方启动服务器,"
                + "另一方输入服务器端的IP地址并连接,"
                + "双方都点击\"Duel\"按钮后游戏即会开始。"
                + "需要结束时点击\"结束对战\"按钮即可与其他玩家再次连接。");
        }

        /// <summary>
        /// handler of the share button
        /// allows the user to share content with his friends
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void share(object sender, RoutedEventArgs e)
        {
            DataTransferManager.ShowShareUI();
        }

        /// <summary>
        /// handler of the showHighScore button
        /// displays the local highscore
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void showHighScore(object sender, RoutedEventArgs e)
        {
            string highScoreString = "";
            for (int i = (int)Difficulty.Low; i <= (int)Difficulty.High; ++i)
            {
                highScoreString += difficultyString[i] + ": ";
                if (highscores[i] == -1)
                {
                    highScoreString += "无";
                }
                else
                {
                    highScoreString += convertMillisecondsToSecondsString(highscores[i]) + "秒";
                }
                highScoreString += "\n";
            }
            messageBox(highScoreString);
        }

        /// <summary>
        /// converts number of milliseconds to a string like "*.*s"
        /// </summary>
        /// <param name="milliseconds"></param>
        /// <returns></returns>
        static private string convertMillisecondsToSecondsString(int milliseconds)
        {
            return (milliseconds / 1000).ToString() + "." + (milliseconds % 1000).ToString();
        }

        /// <summary>
        /// accomplishes for share functionality
        /// collects the content to be shared
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainPage_DataRequested(DataTransferManager sender, DataRequestedEventArgs e)
        {
            DataRequest request = e.Request;

            // Because we are making async calls in the DataRequested event handler,
            // we need to get the deferral first.
            DataRequestDeferral deferral = request.GetDeferral();

            // Make sure we always call Complete on the deferral.
            request.Data.Properties.Title = "最高分";
            request.Data.Properties.Description = "分享战绩";
            string textToShare = "我在扫雷中获得战绩:";
            for (int i = (int)Difficulty.Low; i <= (int)Difficulty.High; ++i)
            {
                textToShare += " ";
                textToShare += difficultyString[i] + ": ";
                if (highscores[i] == -1)
                {
                    textToShare += "无";
                }
                else
                {
                    textToShare += convertMillisecondsToSecondsString(highscores[i]) + "秒";
                }
            }
            request.Data.SetText(textToShare);
            deferral.Complete();
        }
    }
}


text:
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
var core_1 = require("@angular/core");
var forms_1 = require("@angular/forms");
var RangeInput = RangeInput_1 = (function () {
    function RangeInput(elementRef) {
        this.elementRef = elementRef;
    }
    RangeInput.prototype.ngOnChanges = function (changes) {
        if (changes.ngModel) {
            this.inputText = changes.ngModel.currentValue.toString();
        }
    };
    RangeInput.prototype.ngOnInit = function () {
    };
    RangeInput.prototype.validate = function (c) {
        var val = parseInt(this.inputText);
        if (isNaN(val)) {
            return {
                validNumber: {
                    valid: false,
                }
            };
        }
        else if (('undefined' != typeof this.from && this.from > val) ||
            ('undefined' != typeof this.to && this.to < val)) {
            return {
                outOfRange: {
                    valid: false,
                }
            };
        }
    };
    RangeInput.prototype.changedExtraHandler = function (data) {
        if (!this.validate(null)) {
            this._ngModel = parseInt(data);
        }
        this.onChangeHandler(this._ngModel);
    };
    RangeInput.prototype.keyDown = function (event) {
        var keyCode = event.keyCode;
        if (keyCode > 57) {
            event.preventDefault();
            return false;
        }
        if (keyCode >= 48 && keyCode <= 57) {
            var finalValue = parseInt(event.target.value + String.fromCharCode(keyCode));
            if (('undefined' != typeof this.from && this.from > finalValue) ||
                ('undefined' != typeof this.to && this.to < finalValue)) {
                event.preventDefault();
                return false;
            }
            return true;
        }
    };
    RangeInput.prototype.writeValue = function (obj) {
        if ("undefined" != typeof obj && null != obj) {
            this._ngModel = obj;
            this.inputText = this._ngModel.toString();
        }
    };
    RangeInput.prototype.registerOnChange = function (fn) {
        this.onChangeHandler = fn;
    };
    RangeInput.prototype.registerOnTouched = function (fn) {
    };
    return RangeInput;
}());
__decorate([
    core_1.Input(),
    __metadata("design:type", Number)
], RangeInput.prototype, "from", void 0);
__decorate([
    core_1.Input(),
    __metadata("design:type", Number)
], RangeInput.prototype, "to", void 0);
RangeInput = RangeInput_1 = __decorate([
    core_1.Component({
        selector: "internal-range-input",
        template: "<input type=\"text\" [(ngModel)]=\"inputText\" (ngModelChange)=\"changedExtraHandler($event)\"\n                      (keydown)=\"keyDown($event)\">",
        providers: [
            {
                provide: forms_1.NG_VALUE_ACCESSOR,
                useExisting: core_1.forwardRef(function () { return RangeInput_1; }),
                multi: true,
            },
            {
                provide: forms_1.NG_VALIDATORS,
                useExisting: core_1.forwardRef(function () { return RangeInput_1; }),
                multi: true,
            }
        ]
    }),
    __metadata("design:paramtypes", [core_1.ElementRef])
], RangeInput);
exports.RangeInput = RangeInput;
var RangeInput_1;
//# sourceMappingURL=RangeInput.js.map

redpajama_set_name: RedPajamaGithub


text:
	<input class="toggle" type="checkbox" <%= completed ? 'checked' : '' %>>
	<%- title %>
	<button class="destroy"></button>

<input class="edit" value="<%- title %>">


text:
package me.oriley.cutlass;

import android.support.annotation.NonNull;
import android.util.Log;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.inject.Singleton;

@SuppressWarnings("unused")
@Singleton
public final class Injector<T> {

    @NonNull
    private final String mTag;

    @NonNull
    private final T mComponent;

    @NonNull
    private final Class<T> mClass;

    @NonNull
    private final Map<Class, Method> mMethodCache = new HashMap<>();

    public Injector(@NonNull T component, @NonNull Class<T> componentClass) {
        mComponent = component;
        mClass = componentClass;
        mTag = Injector.class.getSimpleName() + "<" + componentClass.getSimpleName() + ">";
        buildCache();
    }

    private void buildCache() {
        mMethodCache.clear();
        for (Method m : mClass.getDeclaredMethods()) {
            Class[] types = m.getParameterTypes();
            if (types.length == 1) {
                m.setAccessible(true);
                mMethodCache.put(types[0], m);
            }
        }
    }

    public void inject(@NonNull Object object) {
        Class currentClass = object.getClass();
        // TODO: Abort early for known base classes?
        while (currentClass != null) {
            Method m = mMethodCache.get(currentClass);
            if (m != null) {
                try {
                    Log.d(mTag, "injection target found for " + currentClass.toString());
                    m.invoke(mComponent, object);
                } catch (Exception e) {
                    Log.e(mTag, "error performing injection for " + object.toString());
                    e.printStackTrace();
                }
                break;
            } else {
                currentClass = currentClass.getSuperclass();
            }
        }
    }
}


text:
<?php
	$page_class = "blog";
	include "includes/header.php";
	$js = 'js/blog.js';

?>

<?php include "./includes/nav.php" ?>

			To your average programmer, the ways of the internet probably seem like common sense. However, to the non-programmers out there, the inner workings of the web can be baffling; so let's take a trip down metaphor lane to avoid any potential confusion.

			In a sense, an 'internet' has existed for hundreds of years, it has just gotten a whole lot faster as of late: If you are like any other person from the late 1800's, you have probably used a mail order catalogue, but let's break down what's actually going on here.

			On one particularly fine afternoon, Ethel was lounging around the house when she decided to pull out her Sears catalogue to buy the fancy new electric radio she'd been eyeing. Little did she know, she simultaneously became the subject of a beautiful extended metaphor.

			Ethel filled out and mailed her order form, violently anxious for her delivery. The mail man collected her letter, giving Ethel a knowing nod as he drove away, and that letter made it's way to the Sears headquarters in Chicago for processing. A worker read the order, walked around the Sears warehouse to gather the necessaries, and prepared the product for shipping.

			The mail man returned and the radio began it's trek back to Ethel. A few days later, with a squeak of joy, Ethel noticed a package on her doorstep. She ripped apart the packaging, and began to follow the instructions to set up the radio set. In no time, Louis Armstrong rang throughout the room, and Ethel sat back to admire her handiwork.

			Ethel is not real.

			Ethel is an internet browser, Sears is a server, the product catalogue is a search engine, and the postal service is the system of modems, routers, and so on that connect you to a server.

			Everything that Ethel did is called "client side," and everything that Sears did is called "server side." Client side "scripts" are instructions that your browser completes (often written with the language JavaScript), and server side scripts are instructions that the server you're communicating with completes (often written with the language PHP).

			Your computer and the servers 'hosting' a website you access communicate in very basic text files, which are merely descriptions of what the browser should draw on the screen; along the lines of "make a big red box that says 'hello world' inside of it." These visual instructions are written with HyperText Markup Language (HTML) which is structure, and Cascading Style Sheets (CSS), which is the styling of that structure.

			In summation:

			HTML: The structure of anything on the web. It's a description of what a page should look like.

			CSS: The style of the structure, works in conjunction with HTML.

			PHP: A server side scripting language, used to pull information from a database, process forms, anything on the server.

			JavaScript: A client side scripting language, executed by an internet browser to do things like animate onscreen elements, define what happens when a button is pressed, etc.

			Server: A computer that stores files and sends them to your browser when requested, as well as execute server side scripts.

			Browser: A program that decodes HTML/CSS and executes other instructions from client side scripts.

			Further reading:

			HowStuffWorks: How does the internet work?

			Wikipedia: Web Development

<?php include "includes/footer.php"; ?>


text:
local modelctx = _G["_MODELCTX"]
assert(modelctx ~= nil, "modelctx nil")
local cfgfile = "lnet.model_http.config."..modelctx

-- global http config
require(cfgfile)

local core = require "lnet.core"
local Sync = core.sync

local proto = require "lnet.proto_http"

local datestr_cache = os.date(config.logs_date_format)
local datestr_time = os.clock()
local function datestr()
	local now = os.clock()
	if now - datestr_time > 0.1 then
		datestr_cache = os.date(config.logs_date_format)
		datestr_time = now
	end
	return datestr_cache
end

local flush_time = os.clock()
local function log_access(req, peer)
	local ua = req.headers["user-agent"] or ""
	local msg = datestr().." - "..peer.ip.." - "..req.headline.." - User-Agent["..ua.."]\r\n"
	local now = os.clock()
	Sync.enter()
	config.access_log:write(msg)
	if now - flush_time > 1 then
		config.access_log:flush()
		flush_time = now
	end
	Sync.leave()
end

local function log_info(info)
	local msg = datestr().." - "..info.."\r\n"
	Sync.enter()
	config.error_log:write(msg)
	config.error_log:flush()
	Sync.leave()
end

local function log_error(err, req, peer)
	local headline = req.headline or ""
	local msg = datestr().." - "..peer.ip.." - "..headline.." - "..err.."\r\n"
	Sync.enter()
	config.error_log:write(msg)
	config.error_log:flush()
	Sync.leave()
end

-- set handler array
-- we map the longest regexp
local urireg = {}
local mapping = {}

local function sethandler()
	mapping = config.uri_mapping
	local i = 1
	for k in pairs(mapping) do
		urireg[i] = k
		i = i + 1
	end
	local f = function(k1, k2) if #k1 > #k2 then return true else return false end end
	table.sort(urireg, f)
end

local function gethandler(uri)
	for _,k in ipairs(urireg) do
		if string.find(uri, k) ~= nil then
			if (mapping[k].chunk == nil) or (not config.code_cache) then
				mapping[k].chunk, mapping[k].err = loadfile(config.http_handler_dir.."/"..mapping[k].handler)
			end
			return mapping[k].chunk, mapping[k].err
		end
	end
end

-- set default http error output
local function set_http_error(http, code, err)
	local desc = "Error"
	if code == 200 then desc = "OK"
	elseif code == 400 then desc = "Bad Request"
	elseif code == 401 then desc = "Unauthorized"
	elseif code == 403 then desc = "Forbidden"
	elseif code == 404 then desc = "Not Found"
	elseif code == 500 then desc = "Internal Error"
	end
	http.resp.code = code
	http.resp.desc = desc
	if config.output_error and (err ~= nil) then
		http.resp.headers["Content-Type"] = "text/plain"
		http.resp.content = "server internal error: "..err
	else
		if 400 <= code and code < 600 then
			http.resp.headers["Content-Type"] = "text/html"
			http.resp.content = '
'..http.resp.code..' '..http.resp.desc..'

'..config.version_info..'
'
		end
	end
end

local function http_response(http, sendfunc)
	local respbuf, err = proto.generate(http.resp)
	if respbuf == nil then
		log_error(err, http.req, http.peer)
	else
		sendfunc(respbuf)
	end
end

-- create a sandbox to do handler
local function sandbox(handler, http, sendfunc)
	local env = {}
	setmetatable(env, {__index = _G})
	setfenv(1, env)
	-- set http to globals for handler
	-- we must save old global http before set!
	local oldhttp = _G.http
	_G.http = http
	handler()
	http_response(http, sendfunc)
	_G.http = oldhttp
end

--
-- chunk start here
--
sethandler()
log_info("["..modelctx.."] worker init")
if not config.code_cache then
	log_info("warning: code cache is off")
end

local CoSocket = require "lnet.cosocket"

local model = {
	-- session timeout time (sec)
	timeout = config.keep_alive or 0,
	-- max http size limit
	size_limit = config.http_size_limit	or 0,
}

-- return (parsed, err)
function model.input(data, peer, sendfunc)
	-- parse http request
	local parsed, err, req = proto.parse(data)
	if parsed < 0 then
		log_error(err, req, peer)
		return -1, err
	elseif parsed == 0 then
		return 0
	end
	log_access(req, peer)
	-- http object
	local http = {
		peer = peer,
		req = req,
		resp = {code = "200", desc = "OK", headers = {["User-Agent"] = config.user_agent}},
		exit = set_http_error,
	}
	-- set default response config
	http.resp.protocol = http.req.protocol
	if config.chunked_mode then
		http.resp.headers["Transfer-Encoding"] = "chunked"
	end
	if http.req.protocol == "HTTP/1.0" then
		if http.req.headers["connection"] == "keep-alive" and config.keep_alive > 0 then
			http.resp.headers["Connection"] = "keep-alive"
		else
			http.resp.headers["Connection"] = "close"
			parsed = -1 -- let server close the connection
		end
	else
		if http.req.headers["connection"] == "close" or config.keep_alive <= 0 then
			http.resp.headers["Connection"] = "close"
			parsed = -1 -- let server close the connection
		else
			http.resp.headers["Connection"] = "keep-alive"
		end
	end
	-- handle http
	local handler, err = gethandler(req.uri)
	if handler == nil then
		log_error(err, req, peer)
		set_http_error(http, 500, err)
		http_response(http, sendfunc)
	else
		-- create coroutine and run
		local func = function () sandbox(handler, http, sendfunc) end
		local errhandler = function (err)
			log_error(err, req, peer)
			set_http_error(http, 500, err)
			http_response(http, sendfunc)
		end
		CoSocket.coroutine(func, errhandler)
	end
	return parsed
end

return model


text:
  1. Download the 0.210 version of Presto using:
  `wget https://repo1.maven.org/maven2/com/facebook/presto/presto-server/0.210/presto-server-0.210.tar.gz`

  2. Extract Presto tar file: `tar zxvf presto-server-0.210.tar.gz`.

  3. Download the Presto CLI for the coordinator and name it presto.

  ```
    wget https://repo1.maven.org/maven2/com/facebook/presto/presto-cli/0.210/presto-cli-0.210-executable.jar

    mv presto-cli-0.210-executable.jar presto

    chmod +x presto
  ```

 ## Create Configuration Files

  1. Create `etc` folder in presto-server-0.210 directory.
  2. Create `config.properties`, `jvm.config`, `log.properties`, and `node.properties` files.
  3. Install uuid to generate a node.id.

      ```
      sudo apt-get install uuid

      uuid
      ```

##### Contents of your node.properties file

  ```
  node.environment=production
  node.id=<generated uuid>
  node.data-dir=/home/ubuntu/data
  ```

##### Contents of your jvm.config file

  ```
  -server
  -Xmx16G
  -XX:+UseG1GC
  -XX:G1HeapRegionSize=32M
  -XX:+UseGCOverheadLimit
  -XX:+ExplicitGCInvokesConcurrent
  -XX:+HeapDumpOnOutOfMemoryError
  -XX:OnOutOfMemoryError=kill -9 %p
  ```

##### Contents of your log.properties file
  ```
  com.facebook.presto=INFO
  ```

 The default minimum level is `INFO`. There are four levels: `DEBUG`, `INFO`, `WARN` and `ERROR`.

## Coordinator Configurations

  ##### Contents of your config.properties
  ```
  coordinator=true
  node-scheduler.include-coordinator=false
  http-server.http.port=8086
  query.max-memory=5GB
  query.max-total-memory-per-node=5GB
  query.max-memory-per-node=3GB
  memory.heap-headroom-per-node=1GB
  discovery-server.enabled=true
  discovery.uri=<coordinator_ip>:8086
  ```
The options `node-scheduler.include-coordinator=false` and `coordinator=true` indicate that the node is the coordinator and tells the coordinator not to do any of the computation work itself and to use the workers.

**Note**: We recommend setting `query.max-memory-per-node` to half of the JVM config max memory, though if your workload is highly concurrent, you may want to use a lower value for `query.max-memory-per-node`.

Also relation between below two configuration-properties should be like:
If, `query.max-memory-per-node=30GB`
Then, `query.max-memory=<30GB * number of nodes>`.

## Worker Configurations

##### Contents of your config.properties

  ```
  coordinator=false
  http-server.http.port=8086
  query.max-memory=5GB
  query.max-memory-per-node=2GB
  discovery.uri=<coordinator_ip>:8086
  ```

**Note**: `jvm.config` and `node.properties` files are same for all the nodes (worker + coordinator). All the nodes should have different `node.id`.

## Catalog Configurations

1. Create a folder named `catalog` in etc directory of presto on all the nodes of the cluster including the coordinator.

##### Configuring Carbondata in Presto
1. Create a file named `carbondata.properties` in the `catalog` folder and set the required properties on all the nodes.

## Add Plugins

1. Create a directory named `carbondata` in plugin directory of presto.
2. Copy `carbondata` jars to `plugin/carbondata` directory on all nodes.

## Start Presto Server on all nodes

```
./presto-server-0.210/bin/launcher start
```
To run it as a background process.

```
./presto-server-0.210/bin/launcher run
```
To run it in foreground.

## Start Presto CLI
```
./presto
```
To connect to carbondata catalog use the following command:

```
./presto --server <coordinator_ip>:8086 --catalog carbondata --schema <schema_name>
```
Execute the following command to ensure the workers are connected.

```
select * from system.runtime.nodes;
```
Now you can use the Presto CLI on the coordinator to query data sources in the catalog using the Presto workers.


text:
The Logo

    You can customize the site's logo by opening _includes/logo.html

    Once there, you can change the text of the logo, use an image, or add whatever you want.

    If you have an image, place the file preferably in the images folder and just use an <img> tag. For example, if your image file is named logo.png, your entire logo.html file will look like this:

<section class="site-logo">
   <a class="site-title-link" href="/">
       <img src="/images/logo.png" alt="Logo alt text">
       <div class="a-left-line"><div class="runner"></div></div>
   </a>
   <div class="site-slogan">A Jekyll Theme</div>
   <div class="a-right-line"></div>
</section>

  YB also offers you the option to have a site slogan. You can change it by modifying the text in the div with the class name of site-slogan. If you do not want to use a slogan, just delete the following lines altogether:

<div class="site-slogan">A Jekyll Theme</div>
<div class="a-right-line"></div>


text:
/**
 * @requires OpenLayers/Map.js
 * @requires OpenLayers/Projection.js
 */

/**
 * Class: OpenLayers.Layer
 */
OpenLayers.Layer = OpenLayers.Class({

    /**
     * APIProperty: id
     * {String}
     */
    id: null,

    /**
     * APIProperty: name
     * {String}
     */
    name: null,

    /**
     * APIProperty: div
     * {DOMElement}
     */
    div: null,

    /**
     * Property: opacity
     * {Float} The layer's opacity. Float number between 0.0 and 1.0.
     */
    opacity: null,

    /**
     * APIProperty: alwaysInRange
     * {Boolean} If a layer's display should not be scale-based, this should
     *     be set to true. This will cause the layer, as an overlay, to always
     *     be 'active', by always returning true from the calculateInRange()
     *     function.
     *
     *     If not explicitly specified for a layer, its value will be
     *     determined on startup in initResolutions() based on whether or not
     *     any scale-specific properties have been set as options on the
     *     layer. If no scale-specific options have been set on the layer, we
     *     assume that it should always be in range.
     *
     *     See #987 for more info.
     */
    alwaysInRange: null,

    /**
     * Constant: EVENT_TYPES
     * {Array(String)} Supported application event types.  Register a listener
     *     for a particular event with the following syntax:
     * (code)
     * layer.events.register(type, obj, listener);
     * (end)
     *
     * Listeners will be called with a reference to an event object.  The
     *     properties of this event depends on exactly what happened.
     *
     * All event objects have at least the following properties:
     * object - {Object} A reference to layer.events.object.
     * element - {DOMElement} A reference to layer.events.element.
     *
     * Supported map event types:
     * loadstart - Triggered when layer loading starts.
     * loadend - Triggered when layer loading ends.
     * loadcancel - Triggered when layer loading is canceled.
     * visibilitychanged - Triggered when layer visibility is changed.
     * move - Triggered when layer moves (triggered with every mousemove
     *     during a drag).
     * moveend - Triggered when layer is done moving, object passed as
     *     argument has a zoomChanged boolean property which tells that the
     *     zoom has changed.
     */
    EVENT_TYPES: ["loadstart", "loadend", "loadcancel", "visibilitychanged",
                  "move", "moveend"],

    /**
     * APIProperty: events
     * {<OpenLayers.Events>}
     */
    events: null,

    /**
     * APIProperty: map
     * {<OpenLayers.Map>} This variable is set when the layer is added to
     *     the map, via the accessor function setMap().
     */
    map: null,

    /**
     * APIProperty: isBaseLayer
     * {Boolean} Whether or not the layer is a base layer. This should be set
     *     individually by all subclasses. Default is false
     */
    isBaseLayer: false,

    /**
     * Property: alpha
     * {Boolean} The layer's images have an alpha channel.  Default is false.
     */
    alpha: false,

    /**
     * APIProperty: displayInLayerSwitcher
     * {Boolean} Display the layer's name in the layer switcher.  Default is
     *     true.
     */
    displayInLayerSwitcher: true,

    /**
     * APIProperty: visibility
     * {Boolean} The layer should be displayed in the map.  Default is true.
     */
    visibility: true,

    /**
     * APIProperty: attribution
     * {String} Attribution string, displayed when an
     *     <OpenLayers.Control.Attribution> has been added to the map.
     */
    attribution: null,

    /**
     * Property: inRange
     * {Boolean} The current map resolution is within the layer's min/max
     *     range. This is set in <OpenLayers.Map.setCenter> whenever the zoom
     *     changes.
     */
    inRange: false,

    /**
     * Propery: imageSize
     * {<OpenLayers.Size>} For layers with a gutter, the image is larger than
     *     the tile by twice the gutter in each dimension.
     */
    imageSize: null,

    /**
     * Property: imageOffset
     * {<OpenLayers.Pixel>} For layers with a gutter, the image offset
     *     represents displacement due to the gutter.
     */
    imageOffset: null,

  // OPTIONS

    /**
     * Property: options
     * {Object} An optional object whose properties will be set on the layer.
     *     Any of the layer properties can be set as a property of the options
     *     object and sent to the constructor when the layer is created.
     */
    options: null,

    /**
     * APIProperty: eventListeners
     * {Object} If set as an option at construction, the eventListeners
     *     object will be registered with <OpenLayers.Events.on>.  Object
     *     structure must be a listeners object as shown in the example for
     *     the events.on method.
     */
    eventListeners: null,

    /**
     * APIProperty: gutter
     * {Integer} Determines the width (in pixels) of the gutter around image
     *     tiles to ignore.  By setting this property to a non-zero value,
     *     images will be requested that are wider and taller than the tile
     *     size by a value of 2 x gutter.  This allows artifacts of rendering
     *     at tile edges to be ignored.  Set a gutter value that is equal to
     *     half the size of the widest symbol that needs to be displayed.
     *     Defaults to zero.  Non-tiled layers always have zero gutter.
     */
    gutter: 0,

    /**
     * APIProperty: projection
     * {<OpenLayers.Projection>} or {<String>} Set in the layer options to
     *     override the default projection string this layer - also set maxExtent,
     *     maxResolution, and units if appropriate. Can be either a string or
     *     an <OpenLayers.Projection> object when created -- will be converted
     *     to an object when setMap is called if a string is passed.
     */
    projection: null,

    /**
     * APIProperty: units
     * {String} The layer map units.  Defaults to 'degrees'.  Possible values
     *     are 'degrees' (or 'dd'), 'm', 'ft', 'km', 'mi', 'inches'.
     */
    units: null,

    /**
     * APIProperty: scales
     * {Array}  An array of map scales in descending order.  The values in the
     *     array correspond to the map scale denominator.  Note that these
     *     values only make sense if the display (monitor) resolution of the
     *     client is correctly guessed by whomever is configuring the
     *     application.  In addition, the units property must also be set.
     *     Use <resolutions> instead wherever possible.
     */
    scales: null,

    /**
     * APIProperty: resolutions
     * {Array} A list of map resolutions (map units per pixel) in descending
     *     order.  If this is not set in the layer constructor, it will be set
     *     based on other resolution related properties (maxExtent,
     *     maxResolution, maxScale, etc.).
     */
    resolutions: null,

    /**
     * APIProperty: maxExtent
     * {<OpenLayers.Bounds>}  The center of these bounds will not stray outside
     *     of the viewport extent during panning.  In addition, if
     *     <displayOutsideMaxExtent> is set to false, data will not be
     *     requested that falls completely outside of these bounds.
     */
    maxExtent: null,

    /**
     * APIProperty: minExtent
     * {<OpenLayers.Bounds>}
     */
    minExtent: null,

    /**
     * APIProperty: maxResolution
     * {Float} Default max is 360 deg / 256 px, which corresponds to
     *     zoom level 0 on gmaps.  Specify a different value in the layer
     *     options if you are not using a geographic projection and
     *     displaying the whole world.
     */
    maxResolution: null,

    /**
     * APIProperty: minResolution
     * {Float}
     */
    minResolution: null,

    /**
     * APIProperty: numZoomLevels
     * {Integer}
     */
    numZoomLevels: null,

    /**
     * APIProperty: minScale
     * {Float}
     */
    minScale: null,

    /**
     * APIProperty: maxScale
     * {Float}
     */
    maxScale: null,

    /**
     * APIProperty: displayOutsideMaxExtent
     * {Boolean} Request map tiles that are completely outside of the max
     *     extent for this layer. Defaults to false.
     */
    displayOutsideMaxExtent: false,

    /**
     * APIProperty: wrapDateLine
     * {Boolean} #487 for more info.
     */
    wrapDateLine: false,

    /**
     * APIProperty: transitionEffect
     * {String} The transition effect to use when the map is panned or
     *     zoomed.
     *
     * There are currently two supported values:
     *  - *null* No transition effect (the default).
     *  - *resize*  Existing tiles are resized on zoom to provide a visual
     *    effect of the zoom having taken place immediately.  As the
     *    new tiles become available, they are drawn over top of the
     *    resized tiles.
     */
    transitionEffect: null,

    /**
     * Property: SUPPORTED_TRANSITIONS
     * {Array} An immutable (that means don't change it!) list of supported
     *     transitionEffect values.
     */
    SUPPORTED_TRANSITIONS: ['resize'],

    /**
     * Constructor: OpenLayers.Layer
     *
     * Parameters:
     * name - {String} The layer name
     * options - {Object} Hashtable of extra options to tag onto the layer
     */
    initialize: function(name, options) {

        this.addOptions(options);

        this.name = name;

        if (this.id == null) {

            this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");

            this.div = OpenLayers.Util.createDiv(this.id);
            this.div.style.width = "100%";
            this.div.style.height = "100%";
            this.div.dir = "ltr";

            this.events = new OpenLayers.Events(this, this.div,
                                                this.EVENT_TYPES);
            if(this.eventListeners instanceof Object) {
                this.events.on(this.eventListeners);
            }

        }

        if (this.wrapDateLine) {
            this.displayOutsideMaxExtent = true;
        }
    },

    /**
     * Method: destroy
     * Destroy is a destructor: this is to alleviate cyclic references which
     *     the Javascript garbage cleaner can not take care of on its own.
     *
     * Parameters:
     * setNewBaseLayer - {Boolean} Set a new base layer when this layer has
     *     been destroyed.  Default is true.
     */
    destroy: function(setNewBaseLayer) {
        if (setNewBaseLayer == null) {
            setNewBaseLayer = true;
        }
        if (this.map != null) {
            this.map.removeLayer(this, setNewBaseLayer);
        }
        this.projection = null;
        this.map = null;
        this.name = null;
        this.div = null;
        this.options = null;

        if (this.events) {
            if(this.eventListeners) {
                this.events.un(this.eventListeners);
            }
            this.events.destroy();
        }
        this.eventListeners = null;
        this.events = null;
    },

   /**
    * Method: clone
    *
    * Parameters:
    * obj - {<OpenLayers.Layer>} The layer to be cloned
    *
    * Returns:
    * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer>
    */
    clone: function (obj) {

        if (obj == null) {
            obj = new OpenLayers.Layer(this.name, this.options);
        }

        // catch any randomly tagged-on properties
        OpenLayers.Util.applyDefaults(obj, this);

        // a cloned layer should never have its map property set
        //  because it has not been added to a map yet.
        obj.map = null;

        return obj;
    },

    /**
     * APIMethod: setName
     * Sets the new layer name for this layer.  Can trigger a changelayer event
     *     on the map.
     *
     * Parameters:
     * newName - {String} The new name.
     */
    setName: function(newName) {
        if (newName != this.name) {
            this.name = newName;
            if (this.map != null) {
                this.map.events.triggerEvent("changelayer", {
                    layer: this,
                    property: "name"
                });
            }
        }
    },

   /**
    * APIMethod: addOptions
    *
    * Parameters:
    * newOptions - {Object}
    */
    addOptions: function (newOptions) {

        if (this.options == null) {
            this.options = {};
        }

        // update our copy for clone
        OpenLayers.Util.extend(this.options, newOptions);

        // add new options to this
        OpenLayers.Util.extend(this, newOptions);
    },

    /**
     * APIMethod: onMapResize
     * This function can be implemented by subclasses
     */
    onMapResize: function() {
        //this function can be implemented by subclasses
    },

    /**
     * APIMethod: redraw
     * Redraws the layer.  Returns true if the layer was redrawn, false if not.
     *
     * Returns:
     * {Boolean} The layer was redrawn.
     */
    redraw: function() {
        var redrawn = false;
        if (this.map) {

            // min/max Range may have changed
            this.inRange = this.calculateInRange();

            // map's center might not yet be set
            var extent = this.getExtent();

            if (extent && this.inRange && this.visibility) {
                var zoomChanged = true;
                this.moveTo(extent, zoomChanged, false);
                this.events.triggerEvent("moveend",
                    {"zoomChanged": zoomChanged});
                redrawn = true;
            }
        }
        return redrawn;
    },

    /**
     * Method: moveTo
     *
     * Parameters:
     * bound - {<OpenLayers.Bounds>}
     * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
     *     do some init work in that case.
     * dragging - {Boolean}
     */
    moveTo:function(bounds, zoomChanged, dragging) {
        var display = this.visibility;
        if (!this.isBaseLayer) {
            display = display && this.inRange;
        }
        this.display(display);
    },

    /**
     * Method: setMap
     * Set the map property for the layer. This is done through an accessor
     *     so that subclasses can override this and take special action once
     *     they have their map variable set.
     *
     *     Here we take care to bring over any of the necessary default
     *     properties from the map.
     *
     * Parameters:
     * map - {<OpenLayers.Map>}
     */
    setMap: function(map) {
        if (this.map == null) {

            this.map = map;

            // grab some essential layer data from the map if it hasn't already
            //  been set
            this.maxExtent = this.maxExtent || this.map.maxExtent;
            this.projection = this.projection || this.map.projection;

            if (this.projection && typeof this.projection == "string") {
                this.projection = new OpenLayers.Projection(this.projection);
            }

            // Check the projection to see if we can get units -- if not, refer
            // to properties.
            this.units = this.projection.getUnits() ||
                         this.units || this.map.units;

            this.initResolutions();

            if (!this.isBaseLayer) {
                this.inRange = this.calculateInRange();
                var show = ((this.visibility) && (this.inRange));
                this.div.style.display = show ? "" : "none";
            }

            // deal with gutters
            this.setTileSize();
        }
    },

    /**
     * Method: afterAdd
     * Called at the end of the map.addLayer sequence.  At this point, the map
     *     will have a base layer.  To be overridden by subclasses.
     */
    afterAdd: function() {
    },

    /**
     * APIMethod: removeMap
     * Just as setMap() allows each layer the possibility to take a
     *     personalized action on being added to the map, removeMap() allows
     *     each layer to take a personalized action on being removed from it.
     *     For now, this will be mostly unused, except for the EventPane layer,
     *     which needs this hook so that it can remove the special invisible
     *     pane.
     *
     * Parameters:
     * map - {<OpenLayers.Map>}
     */
    removeMap: function(map) {
        //to be overridden by subclasses
    },

    /**
     * APIMethod: getImageSize
     *
     * Returns:
     * {<OpenLayers.Size>} The size that the image should be, taking into
     *     account gutters.
     */
    getImageSize: function() {
        return (this.imageSize || this.tileSize);
    },

    /**
     * APIMethod: setTileSize
     * Set the tile size based on the map size.  This also sets layer.imageSize
     *     and layer.imageOffset for use by Tile.Image.
     *
     * Parameters:
     * size - {<OpenLayers.Size>}
     */
    setTileSize: function(size) {
        var tileSize = (size) ? size :
                                ((this.tileSize) ? this.tileSize :
                                                   this.map.getTileSize());
        this.tileSize = tileSize;
        if(this.gutter) {
          // layers with gutters need non-null tile sizes
          //if(tileSize == null) {
          //    OpenLayers.console.error("Error in layer.setMap() for " +
          //                              this.name + ": layers with " +
          //                              "gutters need non-null tile sizes");
          //}
            this.imageOffset = new OpenLayers.Pixel(-this.gutter,
                                                    -this.gutter);
            this.imageSize = new OpenLayers.Size(tileSize.w + (2*this.gutter),
                                                 tileSize.h + (2*this.gutter));
        }
    },

    /**
     * APIMethod: getVisibility
     *
     * Returns:
     * {Boolean} The layer should be displayed (if in range).
     */
    getVisibility: function() {
        return this.visibility;
    },

    /**
     * APIMethod: setVisibility
     * Set the visibility flag for the layer and hide/show & redraw
     *     accordingly. Fire event unless otherwise specified
     *
     * Note that visibility is no longer simply whether or not the layer's
     *     style.display is set to "block". Now we store a 'visibility' state
     *     property on the layer class, this allows us to remember whether or
     *     not we *desire* for a layer to be visible. In the case where the
     *     map's resolution is out of the layer's range, this desire may be
     *     subverted.
     *
     * Parameters:
     * visible - {Boolean} Whether or not to display the layer (if in range)
     */
    setVisibility: function(visibility) {
        if (visibility != this.visibility) {
            this.visibility = visibility;
            this.display(visibility);
            this.redraw();
            if (this.map != null) {
                this.map.events.triggerEvent("changelayer", {
                    layer: this,
                    property: "visibility"
                });
            }
            this.events.triggerEvent("visibilitychanged");
        }
    },

    /**
     * APIMethod: display
     * Hide or show the Layer
     *
     * Parameters:
     * display - {Boolean}
     */
    display: function(display) {
        var inRange = this.calculateInRange();
        if (display != (this.div.style.display != "none")) {
            this.div.style.display = (display && inRange) ? "block" : "none";
        }
    },

    /**
     * APIMethod: calculateInRange
     *
     * Returns:
     * {Boolean} The layer is displayable at the current map's current
     *     resolution. Note that if 'alwaysInRange' is true for the layer,
     *     this function will always return true.
     */
    calculateInRange: function() {
        var inRange = false;

        if (this.alwaysInRange) {
            inRange = true;
        } else {
            if (this.map) {
                var resolution = this.map.getResolution();
                inRange = ( (resolution >= this.minResolution) &&
                            (resolution <= this.maxResolution) );
            }
        }
        return inRange;
    },

    /**
     * APIMethod: setIsBaseLayer
     *
     * Parameters:
     * isBaseLayer - {Boolean}
     */
    setIsBaseLayer: function(isBaseLayer) {
        if (isBaseLayer != this.isBaseLayer) {
            this.isBaseLayer = isBaseLayer;
            if (this.map != null) {
                this.map.events.triggerEvent("changebaselayer", {
                    layer: this
                });
            }
        }
    },

  /********************************************************/
  /*                                                      */
  /*                 Baselayer Functions                  */
  /*                                                      */
  /********************************************************/

    /**
     * Method: initResolutions
     * This method's responsibility is to set up the 'resolutions' array
     *     for the layer -- this array is what the layer will use to interface
     *     between the zoom levels of the map and the resolution display
     *     of the layer.
     *
     * The user has several options that determine how the array is set up.
     *
     * For a detailed explanation, see the following wiki from the
     *     openlayers.org homepage:
     *     http://trac.openlayers.org/wiki/SettingZoomLevels
     */
    initResolutions: function() {

        // These are the relevant options which are used for calculating
        //  resolutions information.
        //
        var props = new Array(
          'projection', 'units',
          'scales', 'resolutions',
          'maxScale', 'minScale',
          'maxResolution', 'minResolution',
          'minExtent', 'maxExtent',
          'numZoomLevels', 'maxZoomLevel'
        );

        //these are the properties which do *not* imply that user wishes
        // this layer to be scale-dependant
        var notScaleProps = ['projection', 'units'];

        //should the layer be scale-dependant? default is false -- this will
        // only be set true if we find that the user has specified a property
        // from the 'props' array that is not in 'notScaleProps'
        var useInRange = false;

        // First we create a new object where we will store all of the
        //  resolution-related properties that we find in either the layer's
        //  'options' array or from the map.
        //
        var confProps = {};
        for(var i=0, len=props.length; i<len; i++) {
            var property = props[i];

            // If the layer had one of these properties set *and* it is
            // a scale property (is not a non-scale property), then we assume
            // the user did intend to use scale-dependant display (useInRange).
            if (this.options[property] &&
                OpenLayers.Util.indexOf(notScaleProps, property) == -1) {
                useInRange = true;
            }

            confProps[property] = this.options[property] || this.map[property];
        }

        //only automatically set 'alwaysInRange' if the user hasn't already
        // set it (to true or false, since the default is null). If user did
        // not intend to use scale-dependant display then we set they layer
        // as alwaysInRange. This means calculateInRange() will always return
        // true and the layer will never be turned off due to scale changes.
        //
        if (this.alwaysInRange == null) {
            this.alwaysInRange = !useInRange;
        }

        // Do not use the scales array set at the map level if
        // either minScale or maxScale or both are set at the
        // layer level
        if ((this.options.minScale != null ||
             this.options.maxScale != null) &&
            this.options.scales == null) {

            confProps.scales = null;
        }
        // Do not use the resolutions array set at the map level if
        // either minResolution or maxResolution or both are set at the
        // layer level
        if ((this.options.minResolution != null ||
             this.options.maxResolution != null) &&
            this.options.resolutions == null) {

            confProps.resolutions = null;
        }

        // If numZoomLevels hasn't been set and the maxZoomLevel *has*,
        //  then use maxZoomLevel to calculate numZoomLevels
        //
        if ( (!confProps.numZoomLevels) && (confProps.maxZoomLevel) ) {
            confProps.numZoomLevels = confProps.maxZoomLevel + 1;
        }

        // First off, we take whatever hodge-podge of values we have and
        //  calculate/distill them down into a resolutions[] array
        //
        if ((confProps.scales != null) || (confProps.resolutions != null)) {
          //preset levels
            if (confProps.scales != null) {
                confProps.resolutions = [];
                for(var i=0, len=confProps.scales.length; i<len; i++) {
                    var scale = confProps.scales[i];
                    confProps.resolutions[i] =
                       OpenLayers.Util.getResolutionFromScale(scale,
                                                              confProps.units);
                }
            }
            confProps.numZoomLevels = confProps.resolutions.length;

        } else {
          //maxResolution and numZoomLevels based calculation

            // determine maxResolution
            if (confProps.minScale) {
                confProps.maxResolution =
                    OpenLayers.Util.getResolutionFromScale(confProps.minScale,
                                                           confProps.units);
            } else if (confProps.maxResolution == "auto") {
                var viewSize = this.map.getSize();
                var wRes = confProps.maxExtent.getWidth() / viewSize.w;
                var hRes = confProps.maxExtent.getHeight()/ viewSize.h;
                confProps.maxResolution = Math.max(wRes, hRes);
            }

            // determine minResolution
            if (confProps.maxScale != null) {
                confProps.minResolution =
                    OpenLayers.Util.getResolutionFromScale(confProps.maxScale,
                                                           confProps.units);
            } else if ( (confProps.minResolution == "auto") &&
                        (confProps.minExtent != null) ) {
                var viewSize = this.map.getSize();
                var wRes = confProps.minExtent.getWidth() / viewSize.w;
                var hRes = confProps.minExtent.getHeight()/ viewSize.h;
                confProps.minResolution = Math.max(wRes, hRes);
            }

            // determine numZoomLevels if not already set on the layer
            // this gives numZoomLevels assuming approximately base 2 scaling
            if (confProps.minResolution != null &&
                this.options.numZoomLevels == undefined) {
                var ratio = confProps.maxResolution / confProps.minResolution;
                confProps.numZoomLevels =
                    Math.floor(Math.log(ratio) / Math.log(2)) + 1;
            }

            // now we have numZoomLevels and maxResolution,
            //  we can populate the resolutions array
            confProps.resolutions = new Array(confProps.numZoomLevels);
            var base = 2;
            if(typeof confProps.minResolution == "number" &&
               confProps.numZoomLevels > 1) {
                /**
                 * If maxResolution and minResolution are set (or related
                 * scale properties), we calculate the base for exponential
                 * scaling that starts at maxResolution and ends at
                 * minResolution in numZoomLevels steps.
                 */
                base = Math.pow(
                    (confProps.maxResolution / confProps.minResolution),
                    (1 / (confProps.numZoomLevels - 1))
                );
            }
            for (var i=0; i < confProps.numZoomLevels; i++) {
                var res = confProps.maxResolution / Math.pow(base, i);
                confProps.resolutions[i] = res;
            }
        }

        //sort resolutions array ascendingly
        //
        confProps.resolutions.sort( function(a, b) { return(b-a); } );

        // now set our newly calculated values back to the layer
        //  Note: We specifically do *not* set them to layer.options, which we
        //        will preserve as it was when we added this layer to the map.
        //        this way cloned layers reset themselves to new map div
        //        dimensions)
        //

        this.resolutions = confProps.resolutions;
        this.maxResolution = confProps.resolutions[0];
        var lastIndex = confProps.resolutions.length - 1;
        this.minResolution = confProps.resolutions[lastIndex];

        this.scales = [];
        for(var i=0, len=confProps.resolutions.length; i<len; i++) {
            this.scales[i] =
               OpenLayers.Util.getScaleFromResolution(confProps.resolutions[i],
                                                      confProps.units);
        }
        this.minScale = this.scales[0];
        this.maxScale = this.scales[this.scales.length - 1];

        this.numZoomLevels = confProps.numZoomLevels;
    },

    /**
     * APIMethod: getResolution
     *
     * Returns:
     * {Float} The currently selected resolution of the map, taken from the
     *     resolutions array, indexed by current zoom level.
     */
    getResolution: function() {
        var zoom = this.map.getZoom();
        return this.getResolutionForZoom(zoom);
    },

    /**
     * APIMethod: getExtent
     *
     * Returns:
     * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat
     *     bounds of the current viewPort.
     */
    getExtent: function() {
        // just use stock map calculateBounds function -- passing no arguments
        //  means it will user map's current center & resolution
        //
        return this.map.calculateBounds();
    },

    /**
     * APIMethod: getZoomForExtent
     *
     * Parameters:
     * bounds - {<OpenLayers.Bounds>}
     * closest - {Boolean} Find the zoom level that most closely fits the
     *     specified bounds. Note that this may result in a zoom that does
     *     not exactly contain the entire extent.
     *     Default is false.
     *
     * Returns:
     * {Integer} The index of the zoomLevel (entry in the resolutions array)
     *     for the passed-in extent. We do this by calculating the ideal
     *     resolution for the given extent (based on the map size) and then
     *     calling getZoomForResolution(), passing along the 'closest'
     *     parameter.
     */
    getZoomForExtent: function(extent, closest) {
        var viewSize = this.map.getSize();
        var idealResolution = Math.max( extent.getWidth()  / viewSize.w,
                                        extent.getHeight() / viewSize.h );

        return this.getZoomForResolution(idealResolution, closest);
    },

    /**
     * Method: getDataExtent
     * Calculates the max extent which includes all of the data for the layer.
     *     This function is to be implemented by subclasses.
     *
     * Returns:
     * {<OpenLayers.Bounds>}
     */
    getDataExtent: function () {
        //to be implemented by subclasses
    },

    /**
     * APIMethod: getResolutionForZoom
     *
     * Parameter:
     * zoom - {Float}
     *
     * Returns:
     * {Float} A suitable resolution for the specified zoom.
     */
    getResolutionForZoom: function(zoom) {
        zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));
        var resolution;
        if(this.map.fractionalZoom) {
            var low = Math.floor(zoom);
            var high = Math.ceil(zoom);
            resolution = this.resolutions[low] -
                ((zoom-low) * (this.resolutions[low]-this.resolutions[high]));
        } else {
            resolution = this.resolutions[Math.round(zoom)];
        }
        return resolution;
    },

    /**
     * APIMethod: getZoomForResolution
     *
     * Parameters:
     * resolution - {Float}
     * closest - {Boolean} Find the zoom level that corresponds to the absolute
     *     closest resolution, which may result in a zoom whose corresponding
     *     resolution is actually smaller than we would have desired (if this
     *     is being called from a getZoomForExtent() call, then this means that
     *     the returned zoom index might not actually contain the entire
     *     extent specified... but it'll be close).
     *     Default is false.
     *
     * Returns:
     * {Integer} The index of the zoomLevel (entry in the resolutions array)
     *     that corresponds to the best fit resolution given the passed in
     *     value and the 'closest' specification.
     */
    getZoomForResolution: function(resolution, closest) {
        var zoom;
        if(this.map.fractionalZoom) {
            var lowZoom = 0;
            var highZoom = this.resolutions.length - 1;
            var highRes = this.resolutions[lowZoom];
            var lowRes = this.resolutions[highZoom];
            var res;
            for(var i=0, len=this.resolutions.length; i<len; ++i) {
                res = this.resolutions[i];
                if(res >= resolution) {
                    highRes = res;
                    lowZoom = i;
                }
                if(res <= resolution) {
                    lowRes = res;
                    highZoom = i;
                    break;
                }
            }
            var dRes = highRes - lowRes;
            if(dRes > 0) {
                zoom = lowZoom + ((highRes - resolution) / dRes);
            } else {
                zoom = lowZoom;
            }
        } else {
            var diff;
            var minDiff = Number.POSITIVE_INFINITY;
            for(var i=0, len=this.resolutions.length; i<len; i++) {
                if (closest) {
                    diff = Math.abs(this.resolutions[i] - resolution);
                    if (diff > minDiff) {
                        break;
                    }
                    minDiff = diff;
                } else {
                    if (this.resolutions[i] < resolution) {
                        break;
                    }
                }
            }
            zoom = Math.max(0, i-1);
        }
        return zoom;
    },

    /**
     * APIMethod: getLonLatFromViewPortPx
     *
     * Parameters:
     * viewPortPx - {<OpenLayers.Pixel>}
     *
     * Returns:
     * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in
     *     view port <OpenLayers.Pixel>, translated into lon/lat by the layer.
     */
    getLonLatFromViewPortPx: function (viewPortPx) {
        var lonlat = null;
        if (viewPortPx != null) {
            var size = this.map.getSize();
            var center = this.map.getCenter();
            if (center) {
                var res  = this.map.getResolution();

                var delta_x = viewPortPx.x - (size.w / 2);
                var delta_y = viewPortPx.y - (size.h / 2);

                lonlat = new OpenLayers.LonLat(center.lon + delta_x * res ,
                                             center.lat - delta_y * res);

                if (this.wrapDateLine) {
                    lonlat = lonlat.wrapDateLine(this.maxExtent);
                }
            } // else { DEBUG STATEMENT }
        }
        return lonlat;
    },

    /**
     * APIMethod: getViewPortPxFromLonLat
     * Returns a pixel location given a map location.  This method will return
     *     fractional pixel values.
     *
     * Parameters:
     * lonlat - {<OpenLayers.LonLat>}
     *
     * Returns:
     * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in
     *     <OpenLayers.LonLat>,translated into view port pixels.
     */
    getViewPortPxFromLonLat: function (lonlat) {
        var px = null;
        if (lonlat != null) {
            var resolution = this.map.getResolution();
            var extent = this.map.getExtent();
            px = new OpenLayers.Pixel(
                (1/resolution * (lonlat.lon - extent.left)),
                (1/resolution * (extent.top - lonlat.lat))
            );
        }
        return px;
    },

    /**
     * APIMethod: setOpacity
     * Sets the opacity for the entire layer (all images)
     *
     * Parameter:
     * opacity - {Float}
     */
    setOpacity: function(opacity) {
        if (opacity != this.opacity) {
            this.opacity = opacity;
            for(var i=0, len=this.div.childNodes.length; i<len; ++i) {
                var element = this.div.childNodes[i].firstChild;
                OpenLayers.Util.modifyDOMElement(element, null, null, null,
                                                 null, null, null, opacity);
            }
        }
    },

    /**
     * Method: getZIndex
     *
     * Returns:
     * {Integer} the z-index of this layer
     */
    getZIndex: function () {
        return this.div.style.zIndex;
    },

    /**
     * Method: setZIndex
     *
     * Parameters:
     * zIndex - {Integer}
     */
    setZIndex: function (zIndex) {
        this.div.style.zIndex = zIndex;
    },

    /**
     * Method: adjustBounds
     * This function will take a bounds, and if wrapDateLine option is set
     *     on the layer, it will return a bounds which is wrapped around the
     *     world. We do not wrap for bounds which *cross* the
     *     maxExtent.left/right, only bounds which are entirely to the left
     *     or entirely to the right.
     *
     * Parameters:
     * bounds - {<OpenLayers.Bounds>}
     */
    adjustBounds: function (bounds) {

        if (this.gutter) {
            // Adjust the extent of a bounds in map units by the
            // layer's gutter in pixels.
            var mapGutter = this.gutter * this.map.getResolution();
            bounds = new OpenLayers.Bounds(bounds.left - mapGutter,
                                           bounds.bottom - mapGutter,
                                           bounds.right + mapGutter,
                                           bounds.top + mapGutter);
        }

        if (this.wrapDateLine) {
            // wrap around the date line, within the limits of rounding error
            var wrappingOptions = {
                'rightTolerance':this.getResolution()
            };
            bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);

        }
        return bounds;
    },

    CLASS_NAME: "OpenLayers.Layer"
});