using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using GrapeCity.ActiveReports.Aspnetcore.Viewer;
using GrapeCity.ActiveReports.Aspnetcore.Designer;
using System.Text;
using GrapeCity.ActiveReports.Web.Designer;
using WebDesigner_CustomSharedDataSources.Implementation;
using GrapeCity.ActiveReports.Rendering.Data;
using System.Linq;
using System.Globalization;
using GrapeCity.ActiveReports.Web.Viewer;
using GrapeCity.ActiveReports;

namespace WebDesigner_CustomSharedDataSources
{
    public class Startup
	{
		private static readonly DirectoryInfo ResourcesRootDirectory = 
			new DirectoryInfo(Path.Combine(Directory.GetCurrentDirectory(), "resources"));

		public Startup(IConfiguration configuration)
		{
			Configuration = configuration;
		}

		public IConfiguration Configuration { get; }

		// This method gets called by the runtime. Use this method to add services to the container.
		public void ConfigureServices(IServiceCollection services)
		{
			Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

			services
				.AddReportViewer()
				.AddReportDesigner()
				.AddSingleton<IReportStore>(new ReportStore(ResourcesRootDirectory))
				.AddSingleton<IResourceRepositoryProvider>(new ResourceProvider(ResourcesRootDirectory))
				.AddScoped<IReportProvider, ReportProvider>()
				.AddScoped<IReportResourceProvider, ReportResourceProvider>()
				.AddMvc()
				.AddRazorOptions(options => { })
				.AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null);
		}

		// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
		public void Configure(IApplicationBuilder app, 
			IWebHostEnvironment env, 
			IReportStore reportStore, 
			IResourceRepositoryProvider resourceProvider,
			IReportProvider reportProvider,
			IReportResourceProvider reportResourceProvider
			)
		{
			if (env.IsDevelopment())
			{
				app.UseDeveloperExceptionPage();
			}

            app.UseReportViewer(config =>
			{
                config.UseReportProvider(reportProvider);
                config.UseResourceProvider(reportResourceProvider);
                config.UseDataProviders([
                    new GrapeCity.ActiveReports.Web.Viewer.DataProviderInfo("ORACLE",
                        typeof(Oracle.ManagedDataAccess.Client.OracleClientFactory).AssemblyQualifiedName,
                        typeof(OracleDataConnectionAdapter).AssemblyQualifiedName)
                ]);
            });

			app.UseReportDesigner(config =>
			{
				config.UseReportsProvider(reportStore);
				config.UseResourcesProvider(resourceProvider);
				config.UseDataProviders([
					new GrapeCity.ActiveReports.Web.Designer.DataProviderInfo("ORACLE",
						typeof(Oracle.ManagedDataAccess.Client.OracleClientFactory).AssemblyQualifiedName,
						typeof(OracleDataConnectionAdapter).AssemblyQualifiedName)
				]);
			});

            app.UseDefaultFiles();
            app.UseStaticFiles();

			app.UseRouting();

			app.UseEndpoints(endpoints =>
			{
				endpoints.MapRazorPages();
				endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
			});
		}
	}

	public sealed class OracleDataConnectionAdapter : DbConnectionAdapter
	{
		public static OracleDataConnectionAdapter Instance = new OracleDataConnectionAdapter();

		/// <summary>
		/// Returns the string representation of a multi-value parameter's value.
		/// </summary>
		protected override string MultivalueParameterValueToString(object[] parameterArrayValue)
		{
			return string.Join(",", parameterArrayValue.Select(parameterValue => "'" + Convert.ToString(parameterValue, CultureInfo.InvariantCulture) + "'"));
		}
	}

    internal class ReportProvider : IReportProvider
    {
        private readonly IReportStore _reportStore;

        public ReportProvider(IReportStore reportStore)
        {
            _reportStore = reportStore;
        }

        public Stream GetReport(string reportId)
        {
            return _reportStore.LoadReport(reportId);
        }

        public ReportDescriptor GetReportDescriptor(string reportId)
        {
            return _reportStore.GetReportDescriptor(reportId);
        }
    }

    internal class ReportResourceProvider : IReportResourceProvider
    {
        private readonly IResourceRepositoryProvider _resourceRepositoryProvider;

        public ReportResourceProvider(
            IResourceRepositoryProvider resourceRepositoryProvider)
        {
            _resourceRepositoryProvider = resourceRepositoryProvider;
        }

        public Stream GetResource(ResourceInfo resource)
        {
            return _resourceRepositoryProvider.GetResource(resource);
        }
    }
}
