text:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using FlatRedBall.Glue.Plugins.Interfaces;
using FlatRedBall.IO;
using System.IO;
using System.Windows.Forms;
using System.CodeDom.Compiler;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
using Ionic.Zip;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition;

namespace FlatRedBall.Glue.Plugins
{
    #region PluginCategories enum

    public enum PluginCategories
    {
        Global = 1,
        ProjectSpecific = 2,
        All = Global | ProjectSpecific
    }

    #endregion

    public class PluginManagerBase
    {
        protected List<Assembly> mExternalAssemblies = new List<Assembly>();
        protected List<string> mReferenceListInternal = new List<string>();
        protected List<string> mReferenceListLoaded = new List<string>();
        protected List<string> mReferenceListExternal = new List<string>();

        protected Dictionary<IPlugin, PluginContainer> mPluginContainers = new Dictionary<IPlugin, PluginContainer>();

        protected const String ReferenceFileName = "References.txt";
        protected const String CompatibilityFileName = "Compatibility.txt";
        protected bool mError = false;

        protected static PluginManagerBase mGlobalInstance;
        protected static PluginManagerBase mProjectInstance;

        protected static List<PluginManagerBase> mInstances = new List<PluginManagerBase>();
        protected bool mGlobal;

        public bool HasFinishedInitialization = false;

        private static readonly List<Assembly> mAddGlobalOnInitialize = new List<Assembly>();
        public static List<Assembly> AddGlobalOnInitialize
        {
            get { return mAddGlobalOnInitialize; }
        }

        public static PluginManagerBase GetGlobal()
        {
            return mGlobalInstance;
        }

        public static PluginManagerBase GetProject()
        {
            return mProjectInstance;
        }

        public static List<PluginManagerBase> GetInstances()
        {
            return mInstances;
        }

        public static List<PluginContainer> AllPluginContainers
        {
            get
            {
                List<PluginContainer> returnList = new List<PluginContainer>();

                foreach (PluginManagerBase pluginManager in mInstances)
                {
                    returnList.AddRange(pluginManager.mPluginContainers.Values);
                }

                return returnList;
            }
        }

        public Dictionary<IPlugin, PluginContainer> PluginContainers
        {
            get { return mPluginContainers; }
        }

        protected virtual void LoadReferenceLists()
        {
            // We use absolute paths for some of the .dlls and .exes
            // because if we don't, then Glue looks for them in the Startup
            // path, which could depend on whether Glue is launched from a shortcut
            // or not - this is really common for released versions.
            string executablePath = FileManager.GetDirectory(System.Reflection.Assembly.GetAssembly(typeof(PluginManagerBase)).Location);

            //Load Internal List
            AddIfExists(executablePath + "EditorObjectsXna.dll");
            AddIfExists(executablePath + "FlatRedBall.dll");
            AddIfExists(executablePath + "FlatRedBall.Plugin.dll");

            AddIfExists(executablePath + "GlueSaveClasses.dll");
            AddIfExists(executablePath + "Glue.exe");
            AddIfExists(executablePath + "GlueView.exe");
            AddIfExists(executablePath + "GluxViewManager.dll");
            AddIfExists(executablePath + "Ionic.Zip.dll");
            // I think this is automatcially handled by Glue
            //AddIfExists(executablePath + "InteractiveInterface.dll");

            mReferenceListInternal.Add("Microsoft.CSharp.dll");
            mReferenceListInternal.Add("System.dll");
            mReferenceListInternal.Add("System.ComponentModel.Composition.dll");
            mReferenceListInternal.Add("System.Core.dll");
            mReferenceListInternal.Add("System.Data.dll");
            mReferenceListInternal.Add("System.Data.DataSetExtensions.dll");
            mReferenceListInternal.Add("System.Drawing.dll");
            mReferenceListInternal.Add("System.Windows.Forms.dll");
            mReferenceListInternal.Add("System.Xml.dll");
            mReferenceListInternal.Add("System.Xml.Linq.dll");

            mReferenceListLoaded.Add("Microsoft.Xna.Framework.dll");
            mReferenceListLoaded.Add("Microsoft.Xna.Framework.Graphics.dll");
            mReferenceListLoaded.Add("Microsoft.Xna.Framework.Game.dll");
            mReferenceListLoaded.Add("Microsoft.Xna.Framework.Content.Pipeline.dll");
        }

        protected void AddIfExists(string absoluteName)
        {
            if (File.Exists(absoluteName))
            {
                mReferenceListInternal.Add(absoluteName);
            }
        }

        protected void LoadExternalReferenceList(string filePath)
        {
            string ReferenceFilePath = filePath + "\\" + ReferenceFileName;
            mReferenceListExternal = new List<string>();

            if (File.Exists(ReferenceFilePath))
            {
                using (StreamReader file = new StreamReader(ReferenceFilePath))
                {
                    string line;

                    while ((line = file.ReadLine()) != null)
                    {
                        if (!String.IsNullOrEmpty(line) &&
                           !String.IsNullOrEmpty(line.Trim()))
                        {
                            if (FileManager.FileExists(line.Trim()))
                            {
                                string absolute = FileManager.MakeAbsolute(line.Trim());

                                if (!mReferenceListInternal.Contains(absolute))
                                {
                                    mReferenceListExternal.Add(absolute);
                                    mExternalAssemblies.Add(Assembly.LoadFrom(absolute));
                                }
                            }
                        }
                    }
                }
            }
        }

        public PluginManagerBase(bool global)
        {
            mGlobal = global;
        }

        protected virtual void InstantiateAllListsAsEmpty()
        {

        }

        protected static bool PopulateCatalog(PluginManagerBase instance, string pluginFolderInAppData, List<string> pluginsToIgnore = null)
        {
            instance.mError = false;

            ResolveEventHandler reh = new ResolveEventHandler(instance.currentDomain_AssemblyResolve);
            bool succeeded = true;
            try
            {
                AppDomain currentDomain = AppDomain.CurrentDomain;
                //currentDomain.AssemblyResolve += reh;

                AggregateCatalog catalog = instance.CreateCatalog(pluginFolderInAppData, pluginsToIgnore);

                // Before we compose the parts, we want to pull out any .dll that doesn't have its references satisfied
                // Update : this isn't going to work because sometimes a .dll references another .dll, but the other .dll
                // isn't used.
                //CleanUnreferencedDlls(catalog, instance);

                    var container = new CompositionContainer(catalog);

                    container.ComposeParts(instance);

                succeeded = true;

            }
            catch (Exception e)
            {
                // If we get here then output won't even print out or even load.
                // This invalidates all plugins, so we should show ane rror.
                MessageBox.Show("Error in a plugin that shut down the plugin system: \n\n" + e.ToString());

                instance.CompileErrors.Add("Error trying to load plugins: \r\n\r\n" + e.ToString());

                instance.InstantiateAllListsAsEmpty();

                succeeded = false;
            }
            finally
            {
                AppDomain.CurrentDomain.AssemblyResolve -= reh;
            }
            return succeeded;
        }

        //private static void CleanUnreferencedDlls(AggregateCatalog catalog, PluginManagerBase instance)
        //{
        //    List<string> allFiles = new List<string>();
        //    allFiles.AddRange(
        //        instance.mReferenceListExternal.Select(item => FileManager.RemoveExtension(FileManager.RemovePath(item))));
        //    allFiles.AddRange(
        //        instance.mReferenceListInternal.Select(item => FileManager.RemoveExtension(FileManager.RemovePath(item))));
        //    allFiles.AddRange(
        //        instance.mReferenceListLoaded.Select(item => FileManager.RemoveExtension(FileManager.RemovePath(item))));

        //    allFiles.AddRange(catalog.Catalogs.Select
        //        (item=>FileManager.RemovePath((item as AssemblyCatalog).Assembly.GetName().Name)));
        //    allFiles.Add("mscorlib");

        //    List<string> assembliesToSkip = new List<string>()
        //    {
        //        "Glue",
        //        "FlatRedBall.Plugin"

        //    };

        //    for (int i = catalog.Catalogs.Count - 1; i > -1; i--)
        //    {
        //        Assembly assembly = (catalog.Catalogs.ElementAt(i) as AssemblyCatalog).Assembly;

        //        if (assembliesToSkip.Contains(assembly.GetName().Name) == false)
        //        {
        //            foreach (var toFind in assembly.GetReferencedAssemblies())
        //            {

        //                string toFindName = toFind.Name;

        //                // We skip over stuff that we know is always there

        //                bool found = allFiles.Contains(toFindName);

        //                if (!found)
        //                {
        //                    int m = 3;
        //                }
        //            }
        //        }
        //    }
        //}

        private Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            foreach (Assembly item in mExternalAssemblies)
            {
                if (item.FullName == args.Name)
                {
                    return item;
                }
            }

            return null;
        }

        private AggregateCatalog CreateCatalog(string folderInAppData, List<string> pluginsToIgnore = null)
        {

            mExternalAssemblies.Clear();
            LoadReferenceLists();

            var returnValue = new AggregateCatalog();

            var pluginDirectories = GetPluginDirectories(folderInAppData);

            foreach (
                var directory in pluginDirectories)
            {
                AddPluginsFromDirectory(pluginsToIgnore, returnValue, directory);
            }

            if (mGlobal)
            {
                Assembly executingAssembly = System.Reflection.Assembly.GetExecutingAssembly();
                var newCatalog = new AssemblyCatalog(executingAssembly);
                returnValue.Catalogs.Add(newCatalog);

                Assembly entryAssembly = System.Reflection.Assembly.GetEntryAssembly();
                if (entryAssembly != executingAssembly && entryAssembly != null)
                {
                    newCatalog = new AssemblyCatalog(entryAssembly);
                    returnValue.Catalogs.Add(newCatalog);

                }

                foreach (var assembly in AddGlobalOnInitialize)
                {
                    newCatalog = new AssemblyCatalog(assembly);

                    returnValue.Catalogs.Add(newCatalog);

                }
            }

            return returnValue;
        }

        private List<string> GetPluginDirectories(string folderInAppData)
        {
            var pluginDirectories = new List<string>();

            if (mGlobal)
            {
                var paths = new List<string>
                                         {
                                             FileManager.GetDirectory(Application.ExecutablePath) + "Plugins",
                                             FileManager.UserApplicationData + folderInAppData
                                         };
                // Glue startup is super verbose, we can quite it down now that plugins seem to be working fine:
                //foreach (string path in paths)
                //{
                //    CompilePluginOutput("Looking for plugins in " + path);
                //}

                pluginDirectories.AddRange(paths.Where(Directory.Exists).SelectMany(Directory.GetDirectories).Select(item=>item + "/"));

                //foreach (string path in pluginDirectories)
                //{
                //    CompilePluginOutput("Found path for plugins " + path);
                //}
            }
            else
            {
                AddDirectoriesForInstance(pluginDirectories);
            }
            return pluginDirectories;
        }

        private void AddPluginsFromDirectory(List<string> pluginsToIgnore, AggregateCatalog returnValue, string plugin)
        {
            try
            {
                bool shouldProcessPlugin = true;
                // GlueView is a special case, so we should skip that
                string pluginToLower = plugin.ToLower().Replace("\\", "/");
                if (pluginToLower.EndsWith("/glueview") || pluginToLower.EndsWith("/glueview/"))
                {
                    shouldProcessPlugin = false;
                }

                if (shouldProcessPlugin)
                {
                    var compileResult = CompilePlugin(plugin);

                    // We had a && false here, so I don't think we use this ignored check, do we?
                    bool isIgnored = pluginsToIgnore != null && pluginsToIgnore.Any(item =>
                        FileManager.Standardize(item.ToLowerInvariant()) == FileManager.Standardize(plugin.ToLowerInvariant()));

                    if (!isIgnored)
                    {
                        bool wasCompiledFromCSharpCode = compileResult != null;

                        if (wasCompiledFromCSharpCode)
                        {
                            HandleAddingDynamicallyCompiledPlugin(returnValue, plugin, compileResult);
                        }

                        // If nothing was compiled check for any dlls to load
                        else
                        {
                            HandleAddingPreCompiledPluginInDirectory(returnValue, plugin);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                CompileErrors.Add("Error loading plugin at " + plugin + "\r\n\r\n" + ex);
            }
        }

        private void HandleAddingPreCompiledPluginInDirectory(AggregateCatalog returnValue, string pluginDirectory)
        {
            List<Assembly> assemblies = new List<Assembly>();

            var assembliesFiles = Directory.GetFiles(pluginDirectory, "*.dll");

            foreach (string assemblyName in assembliesFiles)
            {
                try
                {
                    if (IsAssemblyAlreadyReferenced(assemblyName))
                    {
                        string message = $"Warning: {pluginDirectory} - Skipping over assembly {assemblyName} because it is already loaded by a different plugin";

                        CompileOutput.Add(message);
                    }
                    else
                    {

                        var asm = Assembly.LoadFrom(assemblyName);

                        assemblies.Add(asm);

                    }
                }
                catch (Exception ex)
                {
                    CompileErrors.Add(string.Format("Failed to load {0}: {1}", assemblyName, ex.Message));
                }
            }

            AggregateCatalog catalogToMakeSureStuffIsLinked = new AggregateCatalog();
            foreach (var assembly in assemblies)
            {

                var catalog = new AssemblyCatalog(assembly);
                catalogToMakeSureStuffIsLinked.Catalogs.Add(catalog);
            }

            bool failed = false;
            try
            {
                var container = new CompositionContainer(catalogToMakeSureStuffIsLinked);
                container.GetExports<object>();
            }
            catch (Exception e)
            {
                string message = "";
                message += "Error trying to load plugins from directory:     " + pluginDirectory;

                if (e is ReflectionTypeLoadException)
                {
                    foreach (var innerException in (e as ReflectionTypeLoadException).LoaderExceptions)
                    {
                        message += "\r\n" + innerException.ToString();
                    }
                }
                else
                {
                    message += "\r\n" + e.ToString();
                }
                CompileErrors.Add(message);

                failed = true;
            }

            if (!failed)
            {
                foreach (var assemblyCatalog in catalogToMakeSureStuffIsLinked.Catalogs)
                {
                    returnValue.Catalogs.Add(assemblyCatalog);
                }
            }

        }

        private void HandleAddingDynamicallyCompiledPlugin(AggregateCatalog returnValue, string plugin, CompilerResults compileResult)
        {
            if (compileResult.Errors != null && compileResult.Errors.HasErrors)
            {
                var errors = new StringBuilder();
                var filename = Path.GetFileName(plugin);
                foreach (CompilerError err in compileResult.Errors)
                {
                    errors.Append(string.Format("\r\n{0}({1},{2}): {3}: {4}",
                                filename, err.Line, err.Column,
                                err.ErrorNumber, err.ErrorText));
                }
                CompileErrors.Add("Error loading script " + plugin + "\r\n" + errors.ToString());
            }
            else
            {
                var newCatalog = new AssemblyCatalog(compileResult.CompiledAssembly);

                returnValue.Catalogs.Add(newCatalog);

                //foreach (string assm in mReferenceListExternal)
                //{
                //    Assembly ass = Assembly.LoadFrom(assm);
                //    var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic).Select(a => a.Location);

                //    returnValue.Catalogs.Add(newCatalog);
                //}
            }
        }

        private bool IsAssemblyAlreadyReferenced(string assemblyName)
        {
            string strippedArgumentName = FileManager.RemovePath(assemblyName);

            string fileName = FileManager.RemovePath(assemblyName);

            if(mReferenceListInternal.Contains(fileName))
            {
                return true;
            }

            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

            foreach (Assembly assembly in assemblies)
            {
                if (!assembly.IsDynamic)
                {
                    string strippedName = FileManager.RemovePath(assembly.Location);

                    if (strippedArgumentName == strippedName)
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        protected virtual void AddDirectoriesForInstance(List<string> pluginDirectories)
        {

        }

        private CompilerResults CompilePlugin(string filepath)
        {
            bool hasCs = System.IO.Directory.GetFiles(filepath).Any(item => FileManager.GetDirectory(item) == "cs");

            if(!hasCs)
            {
                return null;
            }

            CompilePluginOutput("Attempting to compile plugin : " + filepath);

            //Reference types to force assembly load
            const TextureProcessorOutputFormat texture = TextureProcessorOutputFormat.Color;
            using (new ZipFile()) { }

            texture.ToString();// We do this to eliminate "is never used" warnings

            string whyIsntCompatible = GetWhyIsntCompatible(filepath);

            if (string.IsNullOrEmpty(whyIsntCompatible))
            {
                LoadExternalReferenceList(filepath);

                string output;
                string name = Assembly.GetEntryAssembly().GetName().Name;
                PluginCompiler.Compiler.Path = System.IO.Path.GetTempPath() + name + @"Compiled\";
                string details;
                // need to fix this, or pass it a root folder or something.
                CompilerResults results = PluginCompiler.Compiler.CompilePlugin(
                    filepath,
                    mReferenceListInternal,
                    mReferenceListLoaded,
                    mReferenceListExternal,
                    out details);

                // Make sure a non-null result was returned
                if (results == null)
                {
                    return null;
                }

                string outputString = "";
                foreach (string s in results.Output)
                {
                    outputString += s + "   ";
                }

                string errorString = "";
                foreach (var s in results.Errors)
                {
                    errorString += s.ToString() + "   ";
                }

                if (!results.Errors.HasErrors && results.CompiledAssembly != null)
                {
                    Type[] types = results.CompiledAssembly.GetTypes();

                    bool foundAnExportedType = false;
                    foreach(Type type in types)
                    {
                        var attributes = type.GetCustomAttributes(typeof(ExportAttribute), true);

                        if (attributes != null && attributes.Length > 0)
                        {
                            foundAnExportedType = true;
                            break;
                        }
                    }

                    if (!foundAnExportedType)
                    {
                        CompilePluginError("Could not find any plugins with the [Export] tag for plugin than " + filepath);
                    }
                }

                string message = null;
                // I don't think we care about this anymore - it just clutters output a ton
                    //"Plugin compile output: " + outputString;
                if (!string.IsNullOrEmpty(errorString))
                {
                    message += "Compile errors: " + errorString;
                }

                CompilePluginError(message);
                return results;
            }
            else
            {
                CompilePluginOutput("Error on plugin in folder " + filepath + " : " + whyIsntCompatible);
            }

            return null;
        }

        protected virtual void CompilePluginOutput(string problem)
        {

        }

        protected virtual void CompilePluginError(string problem)
        {

        }

        private static string GetWhyIsntCompatible(string filepath)
        {
            var compatibilityFilePath = filepath + @"\" + CompatibilityFileName;

            //Check for compatibility file
            if (File.Exists(compatibilityFilePath))
            {
                string value;

                //Get compatibility timestamp
                using (var file = new StreamReader(compatibilityFilePath))
                {
                    value = file.ReadToEnd();
                }

                DateTime compatibilityTime;

                if (DateTime.TryParse(value, out compatibilityTime))
                {
                    DateTime glueTimeStamp = new FileInfo(Assembly.GetExecutingAssembly().Location).LastWriteTime;
                    //If compatibility timestamp is newer than current Glue's timestamp, then don't compile plugin
                    if (glueTimeStamp < compatibilityTime)
                    {
                        return "Glue time stamp is " + glueTimeStamp + " which is not newer than the plugin's time " + compatibilityTime;
                    }
                }
            }

            return null;
        }

        protected void StartupPlugin(IPlugin plugin)
        {
            // See if the plugin already exists - it may implement multiple interfaces
            if (!mPluginContainers.ContainsKey(plugin))
            {
                PluginContainer pluginContainer = new PluginContainer(plugin);
                mPluginContainers.Add(plugin, pluginContainer);

                try
                {
                    plugin.StartUp();
                }
                catch (Exception e)
                {
                    pluginContainer.Fail(e, "Plugin failed in StartUp");
                }
            }
        }

        protected void AddDisabledPlugin(string folder)
        {
            NotLoadedPlugin plugin = new NotLoadedPlugin();
            plugin.FriendlyName = FileManager.RemovePath(folder);
            if(plugin.FriendlyName.EndsWith("/") || plugin.FriendlyName.EndsWith("\\"))
            {
                plugin.FriendlyName = plugin.FriendlyName.Substring(0, plugin.FriendlyName.Length - 1);
            }

            PluginContainer container = new PluginContainer(plugin);

            container.AssemblyLocation = folder + "/unknown.dll";

            mPluginContainers.Add(plugin, container);
            container.IsEnabled = false;

            // don't do any startup or anything
        }

        protected static bool ShouldProcessPluginManager(PluginCategories pluginCategories, PluginManagerBase pluginManager)
        {
            return (pluginManager.mGlobal && (pluginCategories & PluginCategories.Global) == PluginCategories.Global) ||
                                (!pluginManager.mGlobal && (pluginCategories & PluginCategories.ProjectSpecific) == PluginCategories.ProjectSpecific);
        }

        public static bool ShutDownPlugin(IPlugin pluginToShutDown)
        {
            return ShutDownPlugin(pluginToShutDown, PluginShutDownReason.PluginInitiated);
        }

        public static bool ShutDownPlugin(IPlugin pluginToShutDown,
            PluginShutDownReason shutDownReason)
        {
            bool doesPluginWantToShutDown = true;
            PluginContainer container;

            if (mGlobalInstance.mPluginContainers.ContainsKey(pluginToShutDown))
            {
                container = mGlobalInstance.mPluginContainers[pluginToShutDown];
            }
            else
            {
                container = mProjectInstance.mPluginContainers[pluginToShutDown];
            }

            try
            {
                doesPluginWantToShutDown =
                    container.Plugin.ShutDown(shutDownReason);
            }
            catch (Exception exception)
            {
                doesPluginWantToShutDown = true;
            }

            if (doesPluginWantToShutDown)
            {
                container.IsEnabled = false;

                foreach (Control control in container.Controls)
                {
                    // If the plugin hasn't removed the containers then we
                    // need to do the removal here - we don't want old UI floating
                    // around.
                    control.Parent.Controls.Remove(control);
                }

            }

            return doesPluginWantToShutDown;

        }

        protected virtual void StartAllPlugins(List<string> pluginsToIgnore = null)
        {

        }

        public bool LoadPlugins(string pluginFolderInAppData, List<string> pluginsToIgnore = null)
        {
            CompileOutput = new List<string>();
            CompileErrors = new List<string>();

            bool succeeded = PopulateCatalog (this, pluginFolderInAppData, pluginsToIgnore);

            if (succeeded)
            {
                StartAllPlugins(pluginsToIgnore);

            }

            HasFinishedInitialization = true;
            return succeeded;
        }

        public List<string> CompileOutput { get; private set; }
        public List<string> CompileErrors { get; private set; }
        public void RegisterControlForPlugin(Control control, IPlugin owner)
        {
            this.mPluginContainers[owner].Controls.Add(control);
        }
    }
}


text:
.. _g2p:

*************************
Genotype To Phenotype API
*************************

Summary
=======

This API allows users to search for genotype-phenotype
associations in a GA4GH datastore. The user can search for associations
by building queries composed of features, phenotypes, and/or evidence
terms. The API is designed to accommodate search terms specified as
either a string, external identifier, ontology identifier, or as an
'entity' (See Data Model section). These terms are combined as an
``AND`` of ``(feature && phenotype && evidence)``. This flexibility in
the schema allows a variety of data to be stored in the database and
allows users to express a wide range of queries.

Users will receive an array of associations as a response. Associations
contain description and environment fields in addition to the relevant
feature, phenotype, and evidence fields for that instance of
association.

Multiple server collation - Background
--------------------------------------

G2P servers are planned to be implemented in three different contexts:

-  As a wrapper around standalone local G2P "knowledge bases" (eg
   Monarch, CiVIC,etc). Important considerations are the API needs to
   function independently of other parts of the API and separately from
   any specific omics dataset. Often, these databases are not curated
   with complete Feature fields (referenceName, start, end, strand)

.. figure:: https://cloud.githubusercontent.com/assets/47808/14397288/6743ed28-fd91-11e5-9329-66012b722141.png
   :alt: image

-  Coupled with sequence annotation and GA4GH datasets. Clients will
   want implementation specific featureId/genotypeId to match and
   integrate with the rest of the APIs.

.. figure:: https://cloud.githubusercontent.com/assets/47808/14397306/7ad8bb70-fd91-11e5-9295-85c7034ce544.png
   :alt: image

-  Operating in concert with other instances of g2p servers where the
   client's loosely federated query is supported by heterogeneous
   server. Challenges: Normalizing API behavior across implementations
   (featureId for given region different per implementation)

.. figure:: https://cloud.githubusercontent.com/assets/47808/14397316/8a268b8e-fd91-11e5-907d-441fca3450cb.png
   :alt: image

Approach
--------

We based our original work on the model captured in `ga4gh/schemas
commit of Jul 30,
2015 <https://github.com/ga4gh/schemas/tree/be171b00a5f164836dfd40ea5ae75ea56924d316>`__.
This version of the schema predates the `separated genotype to phenotype
files from
baseline <https://github.com/ga4gh/schemas/commit/846b711fdcf544bf889cc7dbab19c6c48e9a9428>`__.
After on review of the schemas and code, the team had feedback about
separation of responsibility in the original API. The API was refactored
to separate the searches for genotype, phenotype, feature and
associations.

Data Model
----------

The cancer genome database `Clinical Genomics Knowledge
Base <http://nif-crawler.neuinfo.org/monarch/ttl/cgd.ttl>`__ published
by the Monarch project was the source of Evidence.

.. figure:: https://cloud.githubusercontent.com/assets/47808/9338065/a0a84b8e-4597-11e5-82ed-65d7b9f3ae97.png
   :alt: image

Intent: The GA4GH Ontology schema provides structures for unambiguous
references to ontological concepts and/or controlled vocabularies
within Protocol Buffers. The structures provided are not intended for
de novo modeling of ontologies, or representing complete ontologies
within Protocol Buffers. References to e.g. classes from external
ontologies or controlled vocabularies should be interpreted only in
their original context i.e. the source ontology.

Due to the flexibility of the data model, users have a number of options
for specifying each query term
`feature <https://github.com/ga4gh/schemas/blob/be171b00a5f164836dfd40ea5ae75ea56924d316/src/main/resources/avro/genotypephenotypemethods.avdl#L105>`__,
`phenotype <https://github.com/ga4gh/schemas/blob/be171b00a5f164836dfd40ea5ae75ea56924d316/src/main/resources/avro/genotypephenotypemethods.avdl#L108>`__,
and
`evidence <https://github.com/ga4gh/schemas/blob/be171b00a5f164836dfd40ea5ae75ea56924d316/src/main/resources/avro/genotypephenotypemethods.avdl#L111>`__.

API
---

The G2P schemas define several endpoints broken into two entity searches
and an association search.

A feature or phenotype can potentially be represented in increasing
specificity as either [a string, an ontology identifier, an external
identifier, or as a feature 'entity']. One criticism of the previous API
is that it was overloaded, violating the design goal of separation of
concerns. Specifically it combines the search for evidence with search
for features & search for genotypes.

The refactored API moves search, alias matching and external identifiers
lookup to dedicated end points. To separate concens, a client performs
the queries for evidence in two steps: first find the desired entities
and then use those enitity identifiers to narrow the search for
evidence.

Additionally the API supports two implementation styles: integrated and
standalone.

.. figure:: ../_static/g2p-sequence-diagram.png
   :alt: sequence

Entity Searches
~~~~~~~~~~~~~~~

-  ``/features/search``

   -  Given a SearchFeaturesRequest, return matching *features* in the
      ``current 'omics dataset``. Intended for sequence annotation and
      GA4GH datasets.

-  ``/phenotypes/search``

   -  Given a SearchPhenotypesRequest, return matching *phenotypes* in
      the in the ``current g2p dataset.``

Association Search
~~~~~~~~~~~~~~~~~~

-  ``/featurephenotypeassociations/search``

   -  Given a SearchGenotypePhenotypeRequest, return matching *evidence
      associations* in the ``current g2p dataset.``

Usage
-----

1. As a GA4GH client, use entity queries for the genotypes and
   phenotypes you are interested in.
2. Create an association search using the entity identifiers from step
   1.
3. Repeat 1-2 as necessary, collating responses on the client.

Many types rely heavily on the concept of an
`OntologyTerm <https://github.com/ga4gh/schemas/blob/be171b00a5f164836dfd40ea5ae75ea56924d316/src/main/resources/avro/ontologies.avdl#L10>`__
(see end of document for discussion on usage of OntologyTerms).

Implementation
--------------

.. figure:: https://cloud.githubusercontent.com/assets/47808/15920813/d70195a0-2dd1-11e6-9c74-ba552735404d.png
   :alt: image

Source Code
~~~~~~~~~~~

-  `Front
   End <https://github.com/ga4gh/server/blob/g2p/ga4gh/frontend.py>`__
   '/features/search', '/datasets/<datasetId>/features/search', '/phenotypes/search',
   '/featurephenotypeassociations/search'
-  `Back
   End <https://github.com/ga4gh/server/blob/g2p/ga4gh/backend.py>`__
   'runSearchFeatures', 'runSearchGenotypePhenotypes', 'runSearchPhenotypes',
   'runSearchGenotypes'
-  `Datamodel <https://github.com/ga4gh/server/blob/g2p/ga4gh/datamodel/genotype_phenotype.py>`__
   'getAssociations'
   `Datamodel <https://github.com/ga4gh/server/blob/g2p/ga4gh/datamodel/genotype_phenotype_featureset.py>`__
   'getAssociations' (Features)

Tests
~~~~~

-  `End to
   End <https://github.com/ga4gh/server/blob/g2p/tests/end_to_end/test_g2p.py>`__

**Help Wanted:** Any or all use cases and scenarios

Acceptance
~~~~~~~~~~

-  Submittal of 3 simultaneous pull-requests for server, schema and
   compliance repositories
-  2 +1s for each repository from outside the development team
-  Additional 3 day review for schemas

API Details and Examples
------------------------

``/phenotypes/search``
~~~~~~~~~~~~~~~~~~~~~~

.. figure:: ../_static/search_phenotypes_request.png
   :alt:

Terms within a query are combined via AND e.g

::

    request = "phenotype": { description:"AML",  "ageOfOnset": {"id": "http://purl.obolibrary.org/obo/HP_0003581"}}

    is transformed by the server to:

    query = (description="AML" and ageOfOnset="http://purl.obolibrary.org/obo/HP_0003581")

Items in the qualifiers array are OR'd together. For example, severe or
abnormal:

::

    request = ... "phenotype": { description:"AML",  "qualifiers": [{"id": "http://purl.obolibrary.org/obo/PATO_0000396"},{"id":"http://purl.obolibrary.org/obo/PATO_0000460"}] } ....

    is transformed by the server to:

    query = (description="AML" and (qualifier = "http://purl.obolibrary.org/obo/PATO_0000460" or qualifier = "http://purl.obolibrary.org/obo/PATO_0000460"))

The service returns a list of matching PhenotypeInstances.

Examples:Phenotype Lookup
^^^^^^^^^^^^^^^^^^^^^^^^^

Q: I have a Disease ontology id ("OBO:OMIM\_606764").

Use an OntologyTerm.

::

    request = { ...  "type": {"id": "http://purl.obolibrary.org/obo/OMIM_606764"}  .... }

The system will respond with phenotypes that match on OntologyTerm.id

Q: I have a phenotype id ("p12345") Create an PhenotypeQuery using id
field.

::

    request = ...  {  "id": "p12345"  } ....

The system will respond with phenotypes that match on
PhenotypeInstance.id

Q: I have an ontology term for a phenotype (HP:0001507, 'Growth
abnormality' )

Use an OntologyTerm.

::

    request = ...  {  "type": {"id": "http://purl.obolibrary.org/obo/HP_0001507"}  } ....

The system will respond with phenotypes that match on OntologyTerm.id

Q: I am only interested in phenotypes qualified with (PATO\_0001899,
``decreased circumference`` ) Create a PhenotypeQuery

::

    request = ...  {  "qualifiers": [{"id": "http://purl.obolibrary.org/obo/PATO_0001899"}] } ....

The system will respond with phenotypes whose qualifiers that match that
ontology 'is\_a'.

Q: I have a disease name "inflammatory bowel disease".

Create an PhenotypeQuery using description field.
``{"description": "inflammatory bowel disease",...}`` The system
responds with Phenotypes that match on OntologyTerm.description Note
that you can wildcard description. ``{"description": ".*bowel.*",...}``
`Supported
regex <https://www.w3.org/TR/xpath-functions/#regex-syntax>`__

--------------

``/features/search``
~~~~~~~~~~~~~~~~~~~~~

This endpoint is provided to serve features/variants/etc hosted by a g2p
dataset when it is deployed independently of the sequenceAnnotations
API. The request and response payloads are identical to
``/datasets/<datasetId>/features/search``.

Terms within a query are combined via AND e.g:

::

    request = { "name":"KIT",  "referenceName": "hg38" }

    becomes

    query = (name="KIT" and referenceName ="hg38")

The service returns a list of matching Features.

Examples:Genotype Lookup
^^^^^^^^^^^^^^^^^^^^^^^^

Note:  since we have switched to relying on the features/search API,
external identifier queries have been deprecated. Refer to features/search documentation.

Q: I have a SNPid ("rs6920220"). Create an External Identifier Query.

``{… {"ids": [{"identifier": "rs6920220", "version": "*", "database": "dbSNP"}]},  … }``

The endpoint will respond with features that match on external
identifier. Multiple identifiers are OR'd together.

Q: I have an identifier for BRCA1 ``GO:0070531`` how do I query for
feature? Create an OntologyTerm query:
``{…   {"type": {"id":"http://purl.obolibrary.org/obo/GO_0070531"},  … }``

The endpoint will respond with features that match on that term.

Q: I only want somatic variant features ``SO:0001777`` how do I limit
results? Specify featureType
``{… {"featureType":"http://purl.obolibrary.org/obo/SO_0001777",  … }``
The endpoint will respond with features that match on that type.

--------------

``/features/search``
~~~~~~~~~~~~~~~~~~~~

See sequence annotations `documentation <sequence_annotations.html>`__.

--------------

``/featurephenotypeassociations/search``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The endpoint accepts a SearchGenotypePhenotypeRequest POST. The request
may contain a feature, phenotype, and/or evidence, which are combined as
a logical AND to query the underlying datastore. Missing types are
treated as a wildcard, returning all data. The genotype and phenotype
fields are either null or a list of identifiers returned from the entity
queries. The evidence query object allows filtering by evidence type.

.. figure:: ../_static/search_genotype_phenotype_request.png
   :alt: http://yuml.me/edit/024cf70f

   http://yuml.me/edit/024cf70f

The SearchGenotypePhenotype search is simplified. Features and
Phenotypes are expressed as a simple array of strings. Evidence can be
queried via the new EvidenceQuery.

The response is returned as a list of associations.

.. figure:: https://raw.githubusercontent.com/ohsu-computational-biology/schemas/a44e67210724af58041fa83c898b1701b53ca84f/doc/source/_static/g2p_response.png
   :alt:

*Implementation Guidance: Results*
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Q: I need a place to store publication identifiers or model machine
learning and statistical data.

The "info" key value pair addition to Evidence.

::

        {
          "evidenceType": {
            "sourceName": "IAO",
            "id": "http://purl.obolibrary.org/obo/IAO_0000311",
            "sourceVersion": null,
            "term": "publication"
          },
          "info": {"source": ["PMID:21470995"]},
          "description": "Associated publication"
        }
        {
          "evidenceType": {
            "sourceName": "OBI",
            "id": "http://purl.obolibrary.org/obo/OBI_0000175",
            "sourceVersion": null,
            "term": "p-value"
          },
          "info": {"p-value": ["1.00e-21"]}
          "description": "Associated p-value"
        },
        {
          "evidenceType": {
            "sourceName": "OBCS",
            "id": "http://purl.obolibrary.org/obo/OBCS_0000054",
            "sourceVersion": null,
            "term": "odds ratio"
          },
          "description": "1.102"
        }

Use cases
---------

1) As a clinician or a genomics researcher, I may have a patient with
   Gastrointestinal stromal tumor, *GIST*, and a proposed drug for
   treatment, *imatinib*. In order to identify whether the patient would
   respond well to treatment with the drug, I need a list of features
   (e.g. genes) which are associated with the sensitivity of *GIST* to
   *imatinib*. Suppose I am specifically interested in a gene, *KIT*,
   which is implicated in the pathogenesis of several cancer types. I
   could submit a query to ``/featurephenotypeassociations/search`` with *GIST* as
   the phenotype, *KIT* as the feature, and
   ``clinical study evidence <http://purl.obolibrary.org/obo/ECO_0000180>``
   as the evidence.

   In response, I will receive back a list of associations involving *GIST*
   and *KIT*, which I can filter for instances where *imatinib* is mentioned.
   URI's in the ``associations`` field could - hypothetically - be followed
   to discover that
   ``GIST patients with wild-type *KIT* have decreased sensitivity to therapy with imatinib   <http://www.ncbi.nlm.nih.gov/pmc/articles/PMC2651076/>``.

   If I left both the ``feature`` and ``evidence`` fields as ``null``, I
   would receive back all associations which involve GIST as a phenotype.

2) As a non-Hodgkin's lymphoma researcher, I may know that the gene
   *CD20* has an abnormal expression in
   ``Hodgkin's lymphoma <http://purl.obolibrary.org/obo/DOID_8567>``.
   I might be interested in knowing whether *CD20* also has an abnormal
   expression in
   ``non-Hodgkin lymphoma <http://purl.obolibrary.org/obo/DOID_0060060>``.
   Therefore I could perform a query with *CD20* as a feature,
   non-Hodgkin's lymphoma as a phenotype, and
   ``RNA sequencing <http://purl.obolibrary.org/obo/OBI_0001177>``
   as the evidence type.

3) As a genetic counselor, I may be wondering if a mutation in one of my
   clients' genes has ever been associated with a disease. I could then
   do a query based on the gene name as the feature and
   ``disease <http://purl.obolibrary.org/obo/DOID_4>`` as the
   phenotype.

For specifics of the json representations, please see the
``server <https://github.com/ga4gh/server>`` and
``compliance <https://github.com/ga4gh/compliance>`` repositories.

Ontologies
----------

**Usage:** Multiple ontology terms can be supplied e.g. to describe a
series of phenotypes for a specific sample. The OntologyTerm message is not
intended to model relationships between terms, or to provide mappings
between ontologies for the same concept. Should an OntologyTerm be
unavailable, or terms unmapped then an 'annotation' can be provided
which can later be mapped to an ontology term using a service designed
for this. Using OntologyTerm is preferred to using Annotation. Though
annotations can be supplied with related ontology terms if desired. A
use case could be when a free text annotation is very specific and a
more general OntologyTerm is supplied.

:ref:`Read more about Ontology Terms<metadata_ontologies>`

--------------

Directions for future capabilities.
===================================

**Flexible representation of Feature**

-  Q: I need to lookup Feature by proteinName or other external id. How do look them up?
      Currently, sequence annotation's features/search supports search by name or location.
      Future versions should implement lookup by alias/

-  Q: I have results from multiple G2P Servers. How do I collate them across datasets and implementations?
      This is a subject for the investigation as we create a federation of G2P servers.
      The responsibility for collating features and associations across servers.
      One strategy might be to use HGVS' DNA annotation for as a neutral identifier for feature.

**Expanding scope to entities other than Feature**

Consider instead a PhenotypeAssociation which has a wider scope; the
objects it connects and the evidence type determines the meaning of the
association

.. figure:: https://cloud.githubusercontent.com/assets/47808/14397329/99fc7c30-fd91-11e5-8346-e95f97bfb78c.png
   :alt: image

redpajama_set_name: RedPajamaGithub


text:
<?xml version="1.0" encoding="utf-8"?>
<com.xiaoshangxing.utils.customView.emotionEdittext.EmotinText xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/only_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/w2"
    android:ellipsize="end"
    android:maxLines="2"
    android:padding="@dimen/x14"
    android:textColor="@color/b0"
    android:textSize="14sp">

</com.xiaoshangxing.utils.customView.emotionEdittext.EmotinText>


text:
<!doctype html>

		<meta charset="utf-8">
		<link rel="stylesheet" href="../dist/reveal.css">
		<link rel="stylesheet" href="../dist/theme/sky.css" id="theme">
		<link rel="stylesheet" href="bulles.css">
		<meta name="author" content="Martin Carmichael" />
		<meta name="copyright" content="© 2022 Martin Carmichael  all rights reserved" />
		<title>Heinz Stein</title>

				Zoe is pregnant

Time for another Music Lesson.

Frank, I'm going to Music School!

				Franken Stein is angry.

I dont want this baby!

Music is the only medicine the heart and soul needs.

I'll give Zoé a little surprise!

				Zoé lost her baby
in an terrible accident.

After the lost of her baby, Zoé gets back to life.

I want a pet, some familiar presence.

Lets go find a new friend.

Love at first sight.

I love her so much!.

				<audio data-autoplay id="audio" src="../snd/monkey.mp3"></audio>

She is so cute !

I will call her Suzanne, a/s "Sue"

She is so lovely!

Zoe brings her new friend to her house.

Franken should be happy.

All right Sue!
Let's go home!

Franken Stein found a new sex toy.

Come on Sue!
Lift your tail!

Bestial, Oral, or Nasal.
Animal sex is so good!

Zoé discovers that Frank Stein raped Suzan

From now on,
Zoé will take care of Suzan.

And your baby...

				Suzan gave birth to a Human-Ape Hybrid male child.

My Son!

My son will be called "Heinz".
Heinz Stein.

Humans. No respect for wildlife.

End of Chapter.

Heinz Stein.

Table of contents


text:
package com.virjar.vscrawler.core.serialize;

import com.virjar.vscrawler.core.processor.GrabResult;
import com.virjar.vscrawler.core.seed.Seed;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;

/**
 * Created by virjar on 17/4/16.
 *
 * @author virjar
 * @since 0.0.1
 */
public class FilePipLine implements Pipeline {
    private String filepath;

    private PrintWriter printWriter;

    public FilePipLine(String filepath) throws FileNotFoundException {
        this.filepath = filepath;
        printWriter = new PrintWriter(new FileOutputStream(new File(filepath), true));
    }

    @Override
    public void saveItem(GrabResult grabResult, Seed seed) {
        for (String str : grabResult.allResult()) {
            printWriter.println(str);
        }
        printWriter.flush();
    }
}


text:
set PI=c:\python26\python.exe

del addlinenum.output
%PI% .\tinyawk.py -f addlinenum.awk addlinenum.awk > addlinenum.output
if ERRORLEVEL 1 goto errorend

del countcomment.output
%PI% .\tinyawk.py -f countcomment.awk countcomment.awk > countcomment.output
if ERRORLEVEL 1 goto errorend

del printfibo.output
%PI% .\tinyawk.py -f printfibo.awk > printfibo.output
if ERRORLEVEL 1 goto errorend

del removedup.output
%PI% .\tinyawk.py -f removedup.awk removedup.awk > removedup.output
if ERRORLEVEL 1 goto errorend

del wc.output
%PI% .\tinyawk.py -f wc.awk wc.awk > wc.output
if ERRORLEVEL 1 goto errorend

goto normalend

:errorend
echo error occured

:normalend


text:
ActiveAdmin.register RadarSetting do

  menu false
  controller do
    nested_belongs_to :event

    def destroy
      destroy!{ admin_event_path(params[:event_id]) }
    end

    def create
      create! do |success, failure|
        success.html do
          redirect_to admin_event_path(params[:event_id])
        end
      end
    end

    def permitted_params
      params.permit(radar_setting: [
        :event_id,
        :url,
        :radar_type
      ])
    end
  end

  form do |f|
    f.inputs "Radar Einstellung" do
      f.input :radar_type, as: :select, collection: ["Meetup", "Onruby", "Ical", "Rss", "Twitter"]
      f.input :url, hint: "Bei Meetup die URL der Gruppe, bei OnRuby die URL zur Startseite der Stadt, bei Twitter die URL zur Seite des Handles, bei iCal/RSS der Link zum jeweiligen Feed"
    end
    f.actions
  end

end


text:
ACCEPTED

#### According to
Index Fungorum

#### Published in
Nuovo Giorn. Bot. Ital. 20: 127 (1888)

#### Original name
Pleospora saccardoi Berl.

### Remarks
null


text:
<?php namespace McCool\Tests\Stubs;

class WronglyDecoratedAtom
{
    public $presenter = 'ThisClassDoesntExistAnywhereInTheKnownUniverse';
}


text:
package at.reality.augmented.vision;

import android.hardware.Camera;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.SurfaceView;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

public class CameraDisplayActivity extends Activity {

	private static final String TAG = CameraDisplayActivity.class.getSimpleName();// DEBUG-Message
	public static Activity act;
//	private static Camera cam;

	private CameraSurface cameraDisplaySurface;
	private UiSurface uiDisplaySurface;

	private FrameLayout frameHolder;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		act = this;

		frameHolder = new FrameLayout(this);

		// force total fullscreen and keep screen on
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		// Inflate the menu; this adds items to the action bar if it is present.
		//getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}

	@Override
	protected void onResume()
	{
		super.onResume();

		frameHolder = new FrameLayout(this);
		frameHolder.setLayoutParams(new LayoutParams(
		           LayoutParams.MATCH_PARENT,
		           LayoutParams.MATCH_PARENT));

		// create SurfaceView for the camera preview and set it visible
		cameraDisplaySurface = new CameraSurface(this);
		frameHolder.addView(cameraDisplaySurface);
		// same for the UiSurface
//		uiDisplaySurface = new UiSurface(this);
//		frameHolder.addView(uiDisplaySurface);

		// and show everything
		setContentView(frameHolder);
	}

	@Override
	protected void onPause()
	{
		super.onPause();
		cameraDisplaySurface = null;
//		uiDisplaySurface = null;
	}

	@Override
	protected void onStop()
	{
		super.onStop();
	}

	@Override
	protected void onDestroy()
	{
		super.onDestroy();
	}
}


text:
package net.sf.esfinge.metadata.TestAnnotationReader;

import java.util.Map;

import net.sf.esfinge.metadata.annotation.container.AnnotationProperty;
import net.sf.esfinge.metadata.annotation.container.ContainerFor;
import net.sf.esfinge.metadata.annotation.container.ContainsAnnotation;
import net.sf.esfinge.metadata.annotation.container.ElementName;
import net.sf.esfinge.metadata.annotation.container.ReflectionReference;
import net.sf.esfinge.metadata.container.ContainerTarget;

@ContainerFor(value = ContainerTarget.CLASS)
public class CT4Container {

	@ContainsAnnotation(Entidade.class)
	private boolean isEntidade;

	@ElementName
	private String nomeClasse;

	private Class<?> classValue;

	@AnnotationProperty(annotation = Tabela.class, property ="nome")
	private String nomeTabela;

	public String getNomeTabela() {
		return nomeTabela;
	}

	public void setNomeTabela(String nomeTabela) {
		this.nomeTabela = nomeTabela;
	}

	public Class<?> getClassValue() {
		return classValue;
	}

	public void setClassValue(Class<?> classValue) {
		this.classValue = classValue;
	}

	public boolean isEntidade() {
		return isEntidade;
	}

	public void setisEntidade(boolean isEntidade) {
		this.isEntidade = isEntidade;
	}

	public String getNomeClasse() {
		return nomeClasse;
	}

	public void setNomeClasse(String nomeClasse) {
		this.nomeClasse = nomeClasse;
	}

}


text:
package com.netflix.dyno.jedis;

import com.netflix.dyno.connectionpool.ConnectionPoolMonitor;
import com.netflix.dyno.connectionpool.impl.ConnectionPoolImpl;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.*;
import redis.clients.jedis.params.GeoRadiusParam;
import redis.clients.jedis.params.ZAddParams;
import redis.clients.jedis.params.ZIncrByParams;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * Dual writer for pipeline commands. This dual writer will apply mutation to both
 * the primary and shadow Dyno clusters but the response returned is only for the
 * primary dynomite cluster. Non-mutation operations are targeted only to primary
 * dynomite clusters.
 */
public class DynoDualWriterPipeline extends DynoJedisPipeline {
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(DynoDualWriterPipeline.class);
    private static ExecutorService executor = Executors.newSingleThreadExecutor();
    private final ConnectionPoolImpl<Jedis> connPool;
    private final DynoJedisPipeline shadowPipeline;
    private final DynoDualWriterClient.Dial dial;

    DynoDualWriterPipeline(String appName,
                           ConnectionPoolImpl<Jedis> pool,
                           DynoJedisPipelineMonitor operationMonitor,
                           ConnectionPoolMonitor connPoolMonitor,
                           ConnectionPoolImpl<Jedis> shadowConnectionPool,
                           DynoDualWriterClient.Dial dial) {
        super(pool, operationMonitor, connPoolMonitor);
        this.connPool = pool;
        this.dial = dial;

        // use a new pipeline monitor for shadow cluster
        int flushTimerFrequency = shadowConnectionPool.getConfiguration().getTimingCountersResetFrequencySeconds();
        DynoJedisPipelineMonitor shadowOperationMonitor = new DynoJedisPipelineMonitor(appName, flushTimerFrequency);
        this.shadowPipeline = new DynoJedisPipeline(shadowConnectionPool, shadowOperationMonitor,
                shadowConnectionPool.getMonitor());
    }

    private ConnectionPoolImpl<Jedis> getConnPool() {
        return this.connPool;
    }

    /*
     * For async scheduling of Jedis commands on shadow clusters.
     */
    private <R> Future<Response<R>> writeAsync(final String key, Callable<Response<R>> func) {
        if (canSendShadowRequest(key)) {
            return executor.submit(func);
        }
        return null;
    }

    /*
     *  For async scheduling of Jedis binary commands on shadow clusters.
     */
    private <R> Future<Response<R>> writeAsync(final byte[] key, Callable<Response<R>> func) {
        if (canSendShadowRequest(key)) {
            return executor.submit(func);
        }
        return null;
    }

    /*
     * Asynchronous processing of non Jedis commands. No dial check as these operations are preceded
     * by Jedis pipeline commands that perform dial check on shadow cluster.
     */
    private <R> Future<R> scheduleAsync(Callable<R> func) {
        return executor.submit(func);
    }

    /**
     * Returns true if the connection pool
     *
Is NOT idle

     *
Has active pools (the shadow cluster may disappear at any time and we don't want to bloat logs)

     *
The key is in range in the dial

     *

     * The idle check is necessary since there may be active host pools however the shadow client may not be able to
     * connect to them, for example, if security groups are not configured properly.
     */
    private boolean canSendShadowRequest(String key) {
        return this.getConnPool().getConfiguration().isDualWriteEnabled() &&
                !this.getConnPool().isIdle() &&
                this.getConnPool().getActivePools().size() > 0 &&
                dial.isInRange(key);
    }

    private boolean canSendShadowRequest(byte[] key) {
        return this.getConnPool().getConfiguration().isDualWriteEnabled() &&
                !this.getConnPool().isIdle() &&
                this.getConnPool().getActivePools().size() > 0 &&
                dial.isInRange(key);
    }

    /**
     * Sync operation will wait for primary cluster and the result is returned. But,
     * on a shadow cluster the operation is asynchronous and the result is not
     * returned to client.
     */
    @Override
    public void sync() {
        scheduleAsync(() -> {
            shadowPipeline.sync();
            return null;
        });
        super.sync();
    }

    @Override
    public List<Object> syncAndReturnAll() {
        scheduleAsync(() -> {
            this.shadowPipeline.sync();
            return null;
        });
        return super.syncAndReturnAll();
    }

    @Override
    public void discardPipelineAndReleaseConnection() {
        scheduleAsync(() -> {
            this.shadowPipeline.discardPipelineAndReleaseConnection();
            return null;
        });
        super.discardPipelineAndReleaseConnection();
    }

    @Override
    public void close() throws Exception {
        this.shadowPipeline.close(); // close the shawdow pipeline synchronously
        super.close();
    }

    //-------------------------- JEDIS PIPELINE COMMANDS ----------------------------

    @Override
    public Response<Long> append(final String key, final String value) {
        writeAsync(key, () -> shadowPipeline.append(key, value));

        return DynoDualWriterPipeline.super.append(key, value);
    }

    @Override
    public Response<List<String>> blpop(final String arg) {
        writeAsync(arg, () -> shadowPipeline.blpop(arg));

        return DynoDualWriterPipeline.super.blpop(arg);
    }

    @Override
    public Response<List<String>> brpop(final String arg) {
        writeAsync(arg, () -> shadowPipeline.brpop(arg));

        return DynoDualWriterPipeline.super.brpop(arg);
    }

    @Override
    public Response<Long> decr(final String key) {
        writeAsync(key, () -> shadowPipeline.decr(key));

        return DynoDualWriterPipeline.super.decr(key);
    }

    @Override
    public Response<Long> decrBy(final String key, final long integer) {
        writeAsync(key, () -> shadowPipeline.decrBy(key, integer));

        return DynoDualWriterPipeline.super.decrBy(key, integer);
    }

    @Override
    public Response<Long> del(final String key) {
        writeAsync(key, () -> shadowPipeline.del(key));

        return DynoDualWriterPipeline.super.del(key);
    }

    @Override
    public Response<Long> expire(final String key, final int seconds) {
        writeAsync(key, () -> shadowPipeline.expire(key, seconds));

        return DynoDualWriterPipeline.super.expire(key, seconds);
    }

    @Override
    public Response<Long> pexpire(String key, long milliseconds) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> expireAt(final String key, final long unixTime) {
        writeAsync(key, () -> shadowPipeline.expireAt(key, unixTime));

        return DynoDualWriterPipeline.super.expireAt(key, unixTime);
    }

    @Override
    public Response<Long> pexpireAt(String key, long millisecondsTimestamp) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> hdel(final String key, final String... field) {
        writeAsync(key, () -> shadowPipeline.hdel(key, field));

        return DynoDualWriterPipeline.super.hdel(key, field);
    }

    @Override
    public Response<Long> hincrBy(final String key, final String field, final long value) {
        writeAsync(key, () -> shadowPipeline.hincrBy(key, field, value));

        return DynoDualWriterPipeline.super.hincrBy(key, field, value);
    }

    /* not supported by RedisPipeline 2.7.3 */
    public Response<Double> hincrByFloat(final String key, final String field, final double value) {
        writeAsync(key, () -> shadowPipeline.hincrByFloat(key, field, value));

        return DynoDualWriterPipeline.super.hincrByFloat(key, field, value);
    }

    /**
     * This method is a BinaryRedisPipeline command which dyno does not yet properly
     * support, therefore the interface is not yet implemented since only a few
     * binary commands are present.
     */
    public Response<String> hmset(final byte[] key, final Map<byte[], byte[]> hash) {
        writeAsync(key, () -> shadowPipeline.hmset(key, hash));

        return DynoDualWriterPipeline.super.hmset(key, hash);
    }

    @Override
    public Response<String> hmset(final String key, final Map<String, String> hash) {
        writeAsync(key, () -> shadowPipeline.hmset(key, hash));

        return DynoDualWriterPipeline.super.hmset(key, hash);
    }

    @Override
    public Response<Long> hset(final String key, final String field, final String value) {
        writeAsync(key, () -> shadowPipeline.hset(key, field, value));

        return DynoDualWriterPipeline.super.hset(key, field, value);
    }

    /**
     * This method is a BinaryRedisPipeline command which dyno does not yet properly
     * support, therefore the interface is not yet implemented.
     */
    public Response<Long> hset(final byte[] key, final byte[] field, final byte[] value) {
        writeAsync(key, () -> shadowPipeline.hset(key, field, value));

        return DynoDualWriterPipeline.super.hset(key, field, value);
    }

    @Override
    public Response<Long> hsetnx(final String key, final String field, final String value) {
        writeAsync(key, () -> shadowPipeline.hsetnx(key, field, value));

        return DynoDualWriterPipeline.super.hsetnx(key, field, value);
    }

    @Override
    public Response<Long> incr(final String key) {
        writeAsync(key, () -> shadowPipeline.incr(key));

        return DynoDualWriterPipeline.super.incr(key);
    }

    @Override
    public Response<Long> incrBy(final String key, final long integer) {
        writeAsync(key, () -> shadowPipeline.incrBy(key, integer));

        return DynoDualWriterPipeline.super.incrBy(key, integer);
    }

    /* not supported by RedisPipeline 2.7.3 */
    public Response<Double> incrByFloat(final String key, final double increment) {
        writeAsync(key, () -> shadowPipeline.incrByFloat(key, increment));

        return DynoDualWriterPipeline.super.incrByFloat(key, increment);
    }

    @Override
    public Response<Long> linsert(final String key, final ListPosition where, final String pivot, final String value) {
        writeAsync(key, () -> shadowPipeline.linsert(key, where, pivot, value));

        return DynoDualWriterPipeline.super.linsert(key, where, pivot, value);
    }

    @Override
    public Response<String> lpop(final String key) {
        writeAsync(key, () -> shadowPipeline.lpop(key));

        return DynoDualWriterPipeline.super.lpop(key);
    }

    @Override
    public Response<Long> lpush(final String key, final String... string) {
        writeAsync(key, () -> shadowPipeline.lpush(key, string));

        return DynoDualWriterPipeline.super.lpush(key, string);
    }

    @Override
    public Response<Long> lpushx(final String key, final String... string) {
        writeAsync(key, () -> shadowPipeline.lpushx(key, string));

        return DynoDualWriterPipeline.super.lpushx(key, string);
    }

    @Override
    public Response<Long> lrem(final String key, final long count, final String value) {
        writeAsync(key, () -> shadowPipeline.lrem(key, count, value));

        return DynoDualWriterPipeline.super.lrem(key, count, value);
    }

    @Override
    public Response<String> lset(final String key, final long index, final String value) {
        writeAsync(key, () -> shadowPipeline.lset(key, index, value));

        return DynoDualWriterPipeline.super.lset(key, index, value);
    }

    @Override
    public Response<String> ltrim(final String key, final long start, final long end) {
        writeAsync(key, () -> shadowPipeline.ltrim(key, start, end));

        return DynoDualWriterPipeline.super.ltrim(key, start, end);
    }

    @Override
    public Response<Long> move(final String key, final int dbIndex) {
        writeAsync(key, () -> shadowPipeline.move(key, dbIndex));

        return DynoDualWriterPipeline.super.move(key, dbIndex);
    }

    @Override
    public Response<Long> persist(final String key) {
        writeAsync(key, () -> shadowPipeline.persist(key));

        return DynoDualWriterPipeline.super.persist(key);
    }

    /* not supported by RedisPipeline 2.7.3 */
    public Response<String> rename(final String oldkey, final String newkey) {
        writeAsync(newkey, () -> shadowPipeline.rename(oldkey, newkey));

        return DynoDualWriterPipeline.super.rename(oldkey, newkey);
    }

    /* not supported by RedisPipeline 2.7.3 */
    public Response<Long> renamenx(final String oldkey, final String newkey) {
        writeAsync(newkey, () -> shadowPipeline.renamenx(oldkey, newkey));

        return DynoDualWriterPipeline.super.renamenx(oldkey, newkey);
    }

    @Override
    public Response<String> rpop(final String key) {
        writeAsync(key, () -> shadowPipeline.rpop(key));

        return DynoDualWriterPipeline.super.rpop(key);
    }

    @Override
    public Response<Long> rpush(final String key, final String... string) {
        writeAsync(key, () -> shadowPipeline.rpush(key, string));

        return DynoDualWriterPipeline.super.rpush(key, string);
    }

    @Override
    public Response<Long> rpushx(final String key, final String... string) {
        writeAsync(key, () -> shadowPipeline.rpushx(key, string));

        return DynoDualWriterPipeline.super.rpushx(key, string);
    }

    @Override
    public Response<Long> sadd(final String key, final String... member) {
        writeAsync(key, () -> shadowPipeline.sadd(key, member));

        return DynoDualWriterPipeline.super.sadd(key, member);
    }

    @Override
    public Response<String> set(final String key, final String value) {
        writeAsync(key, () -> shadowPipeline.set(key, value));

        return DynoDualWriterPipeline.super.set(key, value);
    }

    @Override
    public Response<Boolean> setbit(final String key, final long offset, final boolean value) {
        writeAsync(key, () -> shadowPipeline.setbit(key, offset, value));

        return DynoDualWriterPipeline.super.setbit(key, offset, value);
    }

    @Override
    public Response<String> setex(final String key, final int seconds, final String value) {
        writeAsync(key, () -> shadowPipeline.setex(key, seconds, value));

        return DynoDualWriterPipeline.super.setex(key, seconds, value);
    }

    @Override
    public Response<Long> setnx(final String key, final String value) {
        writeAsync(key, () -> shadowPipeline.setnx(key, value));

        return DynoDualWriterPipeline.super.setnx(key, value);
    }

    @Override
    public Response<Long> setrange(final String key, final long offset, final String value) {
        writeAsync(key, () -> shadowPipeline.setrange(key, offset, value));

        return DynoDualWriterPipeline.super.setrange(key, offset, value);
    }

    @Override
    public Response<List<String>> sort(final String key) {
        writeAsync(key, () -> shadowPipeline.sort(key));

        return DynoDualWriterPipeline.super.sort(key);
    }

    @Override
    public Response<List<String>> sort(final String key, final SortingParams sortingParameters) {
        writeAsync(key, () -> shadowPipeline.sort(key, sortingParameters));

        return DynoDualWriterPipeline.super.sort(key, sortingParameters);
    }

    @Override
    public Response<String> spop(final String key) {
        writeAsync(key, () -> shadowPipeline.spop(key));

        return DynoDualWriterPipeline.super.spop(key);
    }

    @Override
    public Response<Set<String>> spop(final String key, final long count) {
        writeAsync(key, () -> shadowPipeline.spop(key, count));

        return DynoDualWriterPipeline.super.spop(key, count);
    }

    @Override
    public Response<Long> srem(final String key, final String... member) {
        writeAsync(key, () -> shadowPipeline.srem(key, member));

        return DynoDualWriterPipeline.super.srem(key, member);
    }

    /**
     * This method is not supported by the BinaryRedisPipeline interface.
     */
    public Response<ScanResult<String>> sscan(final String key, final int cursor) {
        throw new UnsupportedOperationException("'SSCAN' cannot be called in pipeline");
    }

    /**
     * This method is not supported by the BinaryRedisPipeline interface.
     */
    public Response<ScanResult<String>> sscan(final String key, final String cursor) {
        throw new UnsupportedOperationException("'SSCAN' cannot be called in pipeline");
    }

    @Override
    public Response<Long> zadd(final String key, final double score, final String member) {
        writeAsync(key, () -> shadowPipeline.zadd(key, score, member));

        return DynoDualWriterPipeline.super.zadd(key, score, member);
    }

    @Override
    public Response<Long> zadd(final String key, final Map<String, Double> scoreMembers) {
        writeAsync(key, () -> shadowPipeline.zadd(key, scoreMembers));

        return DynoDualWriterPipeline.super.zadd(key, scoreMembers);
    }

    @Override
    public Response<Double> zincrby(final String key, final double score, final String member) {
        writeAsync(key, () -> shadowPipeline.zincrby(key, score, member));

        return DynoDualWriterPipeline.super.zincrby(key, score, member);
    }

    @Override
    public Response<Long> zrem(final String key, final String... member) {
        writeAsync(key, () -> shadowPipeline.zrem(key, member));

        return DynoDualWriterPipeline.super.zrem(key, member);
    }

    @Override
    public Response<Long> zremrangeByRank(final String key, final long start, final long end) {
        writeAsync(key, () -> shadowPipeline.zremrangeByRank(key, start, end));

        return DynoDualWriterPipeline.super.zremrangeByRank(key, start, end);
    }

    @Override
    public Response<Long> zremrangeByScore(final String key, final double start, final double end) {
        writeAsync(key, () -> shadowPipeline.zremrangeByScore(key, start, end));

        return DynoDualWriterPipeline.super.zremrangeByScore(key, start, end);
    }

    /**
     * This method is not supported by the BinaryRedisPipeline interface.
     */
    public Response<ScanResult<Tuple>> zscan(final String key, final int cursor) {
        throw new UnsupportedOperationException("'ZSCAN' cannot be called in pipeline");
    }

    @Override
    public Response<Long> zlexcount(String key, String min, String max) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<String>> zrangeByLex(String key, String min, String max) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<String>> zrangeByLex(String key, String min, String max, int offset, int count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zremrangeByLex(String key, String start, String end) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    /**** Binary Operations ****/
    @Override
    public Response<String> set(final byte[] key, final byte[] value) {
        writeAsync(key, () -> shadowPipeline.set(key, value));

        return DynoDualWriterPipeline.super.set(key, value);
    }

    @Override
    public Response<Long> pfadd(String key, String... elements) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> pfcount(String key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<Long>> bitfield(String key, String... arguments) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<String>> zrevrangeByLex(String key, String max, String min) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<String>> zrevrangeByLex(String key, String max, String min, int offset, int count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> geoadd(String arg0, Map<String, GeoCoordinate> arg1) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> geoadd(String arg0, double arg1, double arg2, String arg3) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Double> geodist(String arg0, String arg1, String arg2) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Double> geodist(String arg0, String arg1, String arg2, GeoUnit arg3) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<String>> geohash(String arg0, String... arg1) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<GeoCoordinate>> geopos(String arg0, String... arg1) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<GeoRadiusResponse>> georadius(String arg0, double arg1, double arg2, double arg3,
                                                       GeoUnit arg4) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<GeoRadiusResponse>> georadius(String arg0, double arg1, double arg2, double arg3, GeoUnit arg4,
                                                       GeoRadiusParam arg5) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<GeoRadiusResponse>> georadiusByMember(String arg0, String arg1, double arg2, GeoUnit arg3) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<GeoRadiusResponse>> georadiusByMember(String arg0, String arg1, double arg2, GeoUnit arg3,
                                                               GeoRadiusParam arg4) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zadd(String arg0, Map<String, Double> arg1, ZAddParams arg2) {
        throw new UnsupportedOperationException("not yet implemented");

    }

    public Response<Long> zadd(final String key, final double score, final String member, final ZAddParams params) {
        writeAsync(key, () -> shadowPipeline.zadd(key, score, member, params));

        return DynoDualWriterPipeline.super.zadd(key, score, member, params);
    }

    @Override
    public Response<Double> zincrby(String arg0, double arg1, String arg2, ZIncrByParams arg3) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> append(byte[] key, byte[] value) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<byte[]>> blpop(byte[] arg) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<byte[]>> brpop(byte[] arg) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> decr(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> decrBy(final byte[] key, final long integer) {
        writeAsync(key, () -> shadowPipeline.decrBy(key, integer));

        return DynoDualWriterPipeline.super.decrBy(key, integer);
    }

    @Override
    public Response<Long> del(final byte[] key) {
        writeAsync(key, () -> shadowPipeline.del(key));

        return DynoDualWriterPipeline.super.del(key);
    }

    @Override
    public Response<byte[]> echo(byte[] string) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> expire(byte[] key, int seconds) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> pexpire(byte[] key, long milliseconds) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> expireAt(byte[] key, long unixTime) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> pexpireAt(byte[] key, long millisecondsTimestamp) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Boolean> getbit(byte[] key, long offset) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<byte[]> getSet(final byte[] key, final byte[] value) {
        writeAsync(key, () -> shadowPipeline.getSet(key, value));

        return DynoDualWriterPipeline.super.getSet(key, value);
    }

    @Override
    public Response<byte[]> getrange(byte[] key, long startOffset, long endOffset) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> hdel(byte[] key, byte[]... field) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Boolean> hexists(byte[] key, byte[] field) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> hincrBy(byte[] key, byte[] field, long value) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> hkeys(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> hlen(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> hsetnx(byte[] key, byte[] field, byte[] value) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<byte[]>> hvals(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> incr(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> incrBy(byte[] key, long integer) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<byte[]> lindex(byte[] key, long index) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> linsert(byte[] key, ListPosition where, byte[] pivot, byte[] value) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> llen(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<byte[]> lpop(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> lpush(byte[] key, byte[]... string) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> lpushx(byte[] key, byte[]... bytes) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<byte[]>> lrange(byte[] key, long start, long end) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> lrem(byte[] key, long count, byte[] value) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<String> lset(byte[] key, long index, byte[] value) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<String> ltrim(byte[] key, long start, long end) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> move(byte[] key, int dbIndex) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> persist(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<byte[]> rpop(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> rpush(byte[] key, byte[]... string) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> rpushx(byte[] key, byte[]... string) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> sadd(byte[] key, byte[]... member) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> scard(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Boolean> setbit(byte[] key, long offset, byte[] value) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> setrange(byte[] key, long offset, byte[] value) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<String> setex(final byte[] key, final int seconds, final byte[] value) {
        writeAsync(key, () -> shadowPipeline.setex(key, seconds, value));

        return DynoDualWriterPipeline.super.setex(key, seconds, value);
    }

    @Override
    public Response<Long> setnx(byte[] key, byte[] value) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> smembers(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Boolean> sismember(byte[] key, byte[] member) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<byte[]>> sort(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<byte[]>> sort(byte[] key, SortingParams sortingParameters) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<byte[]> spop(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> spop(byte[] key, long count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<byte[]> srandmember(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> srem(byte[] key, byte[]... member) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> strlen(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<String> substr(byte[] key, int start, int end) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> ttl(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<String> type(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zadd(byte[] key, double score, byte[] member) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zadd(byte[] key, double score, byte[] member, ZAddParams params) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zadd(byte[] key, Map<byte[], Double> scoreMembers) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zadd(byte[] key, Map<byte[], Double> scoreMembers, ZAddParams params) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zcard(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zcount(byte[] key, double min, double max) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Double> zincrby(byte[] key, double score, byte[] member) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Double> zincrby(byte[] key, double score, byte[] member, ZIncrByParams params) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrange(byte[] key, long start, long end) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrangeByScore(byte[] key, double min, double max) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrangeByScore(byte[] key, byte[] min, byte[] max) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrangeByScore(byte[] key, double min, double max, int offset, int count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrangeByScore(byte[] key, byte[] min, byte[] max, int offset, int count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<Tuple>> zrangeByScoreWithScores(byte[] key, double min, double max) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<Tuple>> zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<Tuple>> zrangeByScoreWithScores(byte[] key, double min, double max, int offset, int count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<Tuple>> zrangeByScoreWithScores(byte[] key, byte[] min, byte[] max, int offset, int count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrevrangeByScore(byte[] key, double max, double min) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrevrangeByScore(byte[] key, byte[] max, byte[] min) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrevrangeByScore(byte[] key, double max, double min, int offset, int count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrevrangeByScore(byte[] key, byte[] max, byte[] min, int offset, int count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<Tuple>> zrevrangeByScoreWithScores(byte[] key, double max, double min) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<Tuple>> zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<Tuple>> zrevrangeByScoreWithScores(byte[] key, double max, double min, int offset, int count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<Tuple>> zrevrangeByScoreWithScores(byte[] key, byte[] max, byte[] min, int offset, int count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<Tuple>> zrangeWithScores(byte[] key, long start, long end) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zrank(byte[] key, byte[] member) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zrem(byte[] key, byte[]... member) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zremrangeByRank(byte[] key, long start, long end) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zremrangeByScore(byte[] key, double start, double end) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zremrangeByScore(byte[] key, byte[] start, byte[] end) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrevrange(byte[] key, long start, long end) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<Tuple>> zrevrangeWithScores(byte[] key, long start, long end) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zrevrank(byte[] key, byte[] member) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Double> zscore(byte[] key, byte[] member) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zlexcount(byte[] key, byte[] min, byte[] max) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrangeByLex(byte[] key, byte[] min, byte[] max) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrangeByLex(byte[] key, byte[] min, byte[] max, int offset, int count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrevrangeByLex(byte[] key, byte[] max, byte[] min) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Set<byte[]>> zrevrangeByLex(byte[] key, byte[] max, byte[] min, int offset, int count) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> zremrangeByLex(byte[] key, byte[] min, byte[] max) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> bitcount(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> bitcount(byte[] key, long start, long end) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> pfadd(byte[] key, byte[]... elements) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> pfcount(byte[] key) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> geoadd(byte[] key, double longitude, double latitude, byte[] member) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Long> geoadd(byte[] key, Map<byte[], GeoCoordinate> memberCoordinateMap) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Double> geodist(byte[] key, byte[] member1, byte[] member2) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<Double> geodist(byte[] key, byte[] member1, byte[] member2, GeoUnit unit) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<byte[]>> geohash(byte[] key, byte[]... members) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<GeoCoordinate>> geopos(byte[] key, byte[]... members) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<GeoRadiusResponse>> georadius(byte[] key, double longitude, double latitude, double radius,
                                                       GeoUnit unit) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<GeoRadiusResponse>> georadius(byte[] key, double longitude, double latitude, double radius,
                                                       GeoUnit unit, GeoRadiusParam param) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<GeoRadiusResponse>> georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<GeoRadiusResponse>> georadiusByMember(byte[] key, byte[] member, double radius, GeoUnit unit,
                                                               GeoRadiusParam param) {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public Response<List<Long>> bitfield(byte[] key, byte[]... elements) {
        throw new UnsupportedOperationException("not yet implemented");
    }
}