// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.

using System;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using Moq;
using Xunit;
using Assert = Microsoft.TestCommon.AssertEx;

namespace Microsoft.Web.Helpers.Test
{
    public class VideoTest
    {
        private VirtualPathUtilityWrapper _pathUtility = new VirtualPathUtilityWrapper();

        [Fact]
        public void FlashCannotOverrideHtmlAttributes()
        {
            Assert.ThrowsArgument(() => { Video.Flash(GetContext(), _pathUtility, "http://foo.bar.com/foo.swf", htmlAttributes: new { cLASSid = "CanNotOverride" }); }, "htmlAttributes", "Property \"cLASSid\" cannot be set through this argument.");
        }

        [Fact]
        public void FlashDefaults()
        {
            string html = Video.Flash(GetContext(), _pathUtility, "http://foo.bar.com/foo.swf").ToString().Replace("\r\n", "");
            Assert.True(html.StartsWith(
                "<object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" " +
                "codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab\" type=\"application/x-oleobject\" >"
                            ));
            Assert.True(html.Contains("<param name=\"movie\" value=\"http://foo.bar.com/foo.swf\" />"));
            Assert.True(html.Contains("<embed src=\"http://foo.bar.com/foo.swf\" type=\"application/x-shockwave-flash\" />"));
            Assert.True(html.EndsWith("</object>"));
        }

        [Fact]
        public void FlashThrowsWhenPathIsEmpty()
        {
            Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Flash(GetContext(), _pathUtility, String.Empty); }, "path");
        }

        [Fact]
        public void FlashThrowsWhenPathIsNull()
        {
            Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Flash(GetContext(), _pathUtility, null); }, "path");
        }

        [Fact]
        public void FlashWithExposedOptions()
        {
            string html = Video.Flash(GetContext(), _pathUtility, "http://foo.bar.com/foo.swf", width: "100px", height: "100px",
                                      play: false, loop: false, menu: false, backgroundColor: "#000", quality: "Q", scale: "S", windowMode: "WM",
                                      baseUrl: "http://foo.bar.com/", version: "1.0.0.0", htmlAttributes: new { id = "fl" }, embedName: "efl").ToString().Replace("\r\n", "");

            Assert.True(html.StartsWith(
                "<object classid=\"clsid:d27cdb6e-ae6d-11cf-96b8-444553540000\" " +
                "codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=1,0,0,0\" " +
                "height=\"100px\" id=\"fl\" type=\"application/x-oleobject\" width=\"100px\" >"
                            ));
            Assert.True(html.Contains("<param name=\"play\" value=\"False\" />"));
            Assert.True(html.Contains("<param name=\"loop\" value=\"False\" />"));
            Assert.True(html.Contains("<param name=\"menu\" value=\"False\" />"));
            Assert.True(html.Contains("<param name=\"bgColor\" value=\"#000\" />"));
            Assert.True(html.Contains("<param name=\"quality\" value=\"Q\" />"));
            Assert.True(html.Contains("<param name=\"scale\" value=\"S\" />"));
            Assert.True(html.Contains("<param name=\"wmode\" value=\"WM\" />"));
            Assert.True(html.Contains("<param name=\"base\" value=\"http://foo.bar.com/\" />"));

            var embed = new Regex("<embed.*/>").Match(html);
            Assert.True(embed.Success);
            Assert.True(embed.Value.StartsWith("<embed src=\"http://foo.bar.com/foo.swf\" width=\"100px\" height=\"100px\" name=\"efl\" type=\"application/x-shockwave-flash\" "));
            Assert.True(embed.Value.Contains("play=\"False\""));
            Assert.True(embed.Value.Contains("loop=\"False\""));
            Assert.True(embed.Value.Contains("menu=\"False\""));
            Assert.True(embed.Value.Contains("bgColor=\"#000\""));
            Assert.True(embed.Value.Contains("quality=\"Q\""));
            Assert.True(embed.Value.Contains("scale=\"S\""));
            Assert.True(embed.Value.Contains("wmode=\"WM\""));
            Assert.True(embed.Value.Contains("base=\"http://foo.bar.com/\""));
        }

        [Fact]
        public void FlashWithUnexposedOptions()
        {
            string html = Video.Flash(GetContext(), _pathUtility, "http://foo.bar.com/foo.swf", options: new { X = "Y", Z = 123 }).ToString().Replace("\r\n", "");
            Assert.True(html.Contains("<param name=\"X\" value=\"Y\" />"));
            Assert.True(html.Contains("<param name=\"Z\" value=\"123\" />"));
            // note - can't guarantee order of optional params:
            Assert.True(
                html.Contains("<embed src=\"http://foo.bar.com/foo.swf\" type=\"application/x-shockwave-flash\" X=\"Y\" Z=\"123\" />") ||
                html.Contains("<embed src=\"http://foo.bar.com/foo.swf\" type=\"application/x-shockwave-flash\" Z=\"123\" X=\"Y\" />")
                );
        }

        [Fact]
        public void MediaPlayerCannotOverrideHtmlAttributes()
        {
            Assert.ThrowsArgument(() => { Video.MediaPlayer(GetContext(), _pathUtility, "http://foo.bar.com/foo.wmv", htmlAttributes: new { cODEbase = "CanNotOverride" }); }, "htmlAttributes", "Property \"cODEbase\" cannot be set through this argument.");
        }

        [Fact]
        public void MediaPlayerDefaults()
        {
            string html = Video.MediaPlayer(GetContext(), _pathUtility, "http://foo.bar.com/foo.wmv").ToString().Replace("\r\n", "");
            Assert.True(html.StartsWith(
                "<object classid=\"clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6\" >"
                            ));
            Assert.True(html.Contains("<param name=\"URL\" value=\"http://foo.bar.com/foo.wmv\" />"));
            Assert.True(html.Contains("<embed src=\"http://foo.bar.com/foo.wmv\" type=\"application/x-mplayer2\" />"));
            Assert.True(html.EndsWith("</object>"));
        }

        [Fact]
        public void MediaPlayerThrowsWhenPathIsEmpty()
        {
            Assert.ThrowsArgumentNullOrEmptyString(() => { Video.MediaPlayer(GetContext(), _pathUtility, String.Empty); }, "path");
        }

        [Fact]
        public void MediaPlayerThrowsWhenPathIsNull()
        {
            Assert.ThrowsArgumentNullOrEmptyString(() => { Video.MediaPlayer(GetContext(), _pathUtility, null); }, "path");
        }

        [Fact]
        public void MediaPlayerWithExposedOptions()
        {
            string html = Video.MediaPlayer(GetContext(), _pathUtility, "http://foo.bar.com/foo.wmv", width: "100px", height: "100px",
                                            autoStart: false, playCount: 2, uiMode: "UIMODE", stretchToFit: true, enableContextMenu: false, mute: true,
                                            volume: 1, baseUrl: "http://foo.bar.com/", htmlAttributes: new { id = "mp" }, embedName: "emp").ToString().Replace("\r\n", "");
            Assert.True(html.StartsWith(
                "<object classid=\"clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6\" height=\"100px\" id=\"mp\" width=\"100px\" >"
                            ));
            Assert.True(html.Contains("<param name=\"URL\" value=\"http://foo.bar.com/foo.wmv\" />"));
            Assert.True(html.Contains("<param name=\"autoStart\" value=\"False\" />"));
            Assert.True(html.Contains("<param name=\"playCount\" value=\"2\" />"));
            Assert.True(html.Contains("<param name=\"uiMode\" value=\"UIMODE\" />"));
            Assert.True(html.Contains("<param name=\"stretchToFit\" value=\"True\" />"));
            Assert.True(html.Contains("<param name=\"enableContextMenu\" value=\"False\" />"));
            Assert.True(html.Contains("<param name=\"mute\" value=\"True\" />"));
            Assert.True(html.Contains("<param name=\"volume\" value=\"1\" />"));
            Assert.True(html.Contains("<param name=\"baseURL\" value=\"http://foo.bar.com/\" />"));

            var embed = new Regex("<embed.*/>").Match(html);
            Assert.True(embed.Success);
            Assert.True(embed.Value.StartsWith("<embed src=\"http://foo.bar.com/foo.wmv\" width=\"100px\" height=\"100px\" name=\"emp\" type=\"application/x-mplayer2\" "));
            Assert.True(embed.Value.Contains("autoStart=\"False\""));
            Assert.True(embed.Value.Contains("playCount=\"2\""));
            Assert.True(embed.Value.Contains("uiMode=\"UIMODE\""));
            Assert.True(embed.Value.Contains("stretchToFit=\"True\""));
            Assert.True(embed.Value.Contains("enableContextMenu=\"False\""));
            Assert.True(embed.Value.Contains("mute=\"True\""));
            Assert.True(embed.Value.Contains("volume=\"1\""));
            Assert.True(embed.Value.Contains("baseURL=\"http://foo.bar.com/\""));
        }

        [Fact]
        public void MediaPlayerWithUnexposedOptions()
        {
            string html = Video.MediaPlayer(GetContext(), _pathUtility, "http://foo.bar.com/foo.wmv", options: new { X = "Y", Z = 123 }).ToString().Replace("\r\n", "");
            Assert.True(html.Contains("<param name=\"X\" value=\"Y\" />"));
            Assert.True(html.Contains("<param name=\"Z\" value=\"123\" />"));
            Assert.True(
                html.Contains("<embed src=\"http://foo.bar.com/foo.wmv\" type=\"application/x-mplayer2\" X=\"Y\" Z=\"123\" />") ||
                html.Contains("<embed src=\"http://foo.bar.com/foo.wmv\" type=\"application/x-mplayer2\" Z=\"123\" X=\"Y\" />")
                );
        }

        [Fact]
        public void SilverlightCannotOverrideHtmlAttributes()
        {
            Assert.ThrowsArgument(() =>
            {
                Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", "100px", "100px",
                                  htmlAttributes: new { WIDTH = "CanNotOverride" });
            }, "htmlAttributes", "Property \"WIDTH\" cannot be set through this argument.");
        }

        [Fact]
        public void SilverlightDefaults()
        {
            string html = Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", "100px", "100px").ToString().Replace("\r\n", "");
            Assert.True(html.StartsWith(
                "<object data=\"data:application/x-silverlight-2,\" height=\"100px\" type=\"application/x-silverlight-2\" " +
                "width=\"100px\" >"
                            ));
            Assert.True(html.Contains("<param name=\"source\" value=\"http://foo.bar.com/foo.xap\" />"));
            Assert.True(html.Contains(
                "<a href=\"http://go.microsoft.com/fwlink/?LinkID=149156\" style=\"text-decoration:none\">" +
                "<img src=\"http://go.microsoft.com/fwlink?LinkId=108181\" alt=\"Get Microsoft Silverlight\" " +
                "style=\"border-style:none\"/></a>"));
            Assert.True(html.EndsWith("</object>"));
        }

        [Fact]
        public void SilverlightThrowsWhenPathIsEmpty()
        {
            Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Silverlight(GetContext(), _pathUtility, String.Empty, "100px", "100px"); }, "path");
        }

        [Fact]
        public void SilverlightThrowsWhenPathIsNull()
        {
            Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Silverlight(GetContext(), _pathUtility, null, "100px", "100px"); }, "path");
        }

        [Fact]
        public void SilverlightThrowsWhenHeightIsEmpty()
        {
            Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", "100px", String.Empty); }, "height");
        }

        [Fact]
        public void SilverlightThrowsWhenHeightIsNull()
        {
            Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", "100px", null); }, "height");
        }

        [Fact]
        public void SilverlightThrowsWhenWidthIsEmpty()
        {
            Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", String.Empty, "100px"); }, "width");
        }

        [Fact]
        public void SilverlightThrowsWhenWidthIsNull()
        {
            Assert.ThrowsArgumentNullOrEmptyString(() => { Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", null, "100px"); }, "width");
        }

        [Fact]
        public void SilverlightWithExposedOptions()
        {
            string html = Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", width: "85%", height: "85%",
                                            backgroundColor: "red", initParameters: "X=Y", minimumVersion: "1.0.0.0", autoUpgrade: false,
                                            htmlAttributes: new { id = "sl" }).ToString().Replace("\r\n", "");
            Assert.True(html.StartsWith(
                "<object data=\"data:application/x-silverlight-2,\" height=\"85%\" id=\"sl\" " +
                "type=\"application/x-silverlight-2\" width=\"85%\" >"
                            ));
            Assert.True(html.Contains("<param name=\"background\" value=\"red\" />"));
            Assert.True(html.Contains("<param name=\"initparams\" value=\"X=Y\" />"));
            Assert.True(html.Contains("<param name=\"minruntimeversion\" value=\"1.0.0.0\" />"));
            Assert.True(html.Contains("<param name=\"autoUpgrade\" value=\"False\" />"));

            var embed = new Regex("<embed.*/>").Match(html);
            Assert.False(embed.Success);
        }

        [Fact]
        public void SilverlightWithUnexposedOptions()
        {
            string html = Video.Silverlight(GetContext(), _pathUtility, "http://foo.bar.com/foo.xap", width: "50px", height: "50px",
                                            options: new { X = "Y", Z = 123 }).ToString().Replace("\r\n", "");
            Assert.True(html.Contains("<param name=\"X\" value=\"Y\" />"));
            Assert.True(html.Contains("<param name=\"Z\" value=\"123\" />"));
        }

        [Fact]
        public void ValidatePathResolvesExistingLocalPath()
        {
            string path = Assembly.GetExecutingAssembly().Location;
            Mock<VirtualPathUtilityBase> pathUtility = new Mock<VirtualPathUtilityBase>();
            pathUtility.Setup(p => p.Combine(It.IsAny<string>(), It.IsAny<string>())).Returns(path);
            pathUtility.Setup(p => p.ToAbsolute(It.IsAny<string>())).Returns(path);

            Mock<HttpServerUtilityBase> serverMock = new Mock<HttpServerUtilityBase>();
            serverMock.Setup(s => s.MapPath(It.IsAny<string>())).Returns(path);
            HttpContextBase context = GetContext(serverMock.Object);

            string html = Video.Flash(context, pathUtility.Object, "foo.bar").ToString();
            Assert.True(html.StartsWith("<object"));
            Assert.True(html.Contains(HttpUtility.HtmlAttributeEncode(HttpUtility.UrlPathEncode(path))));
        }

        [Fact]
        public void ValidatePathThrowsForNonExistingLocalPath()
        {
            string path = "c:\\does\\not\\exist.swf";
            Mock<VirtualPathUtilityBase> pathUtility = new Mock<VirtualPathUtilityBase>();
            pathUtility.Setup(p => p.Combine(It.IsAny<string>(), It.IsAny<string>())).Returns(path);
            pathUtility.Setup(p => p.ToAbsolute(It.IsAny<string>())).Returns(path);

            Mock<HttpServerUtilityBase> serverMock = new Mock<HttpServerUtilityBase>();
            serverMock.Setup(s => s.MapPath(It.IsAny<string>())).Returns(path);
            HttpContextBase context = GetContext(serverMock.Object);

            Assert.Throws<InvalidOperationException>(() => { Video.Flash(context, pathUtility.Object, "exist.swf"); }, "The media file \"exist.swf\" does not exist.");
        }

        private static HttpContextBase GetContext(HttpServerUtilityBase serverUtility = null)
        {
            // simple mocked context - won't reference as long as path starts with 'http'
            Mock<HttpRequestBase> requestMock = new Mock<HttpRequestBase>();
            Mock<HttpContextBase> contextMock = new Mock<HttpContextBase>();
            contextMock.Setup(context => context.Request).Returns(requestMock.Object);
            contextMock.Setup(context => context.Server).Returns(serverUtility);
            return contextMock.Object;
        }
    }
}