import asyncio, copy, enum, io, types

from .connection import Connection

    
class Database(object):
  
  def __init__(self, src, dialect=None):
    self.dialect = dialect
    self.src = src
  
  @property
  def connection(self):
    return Connection(self, self.src)
  

class EchoDatabase(Database):
  
  def __init__(self):
    self.dialect = Dialect.GENERIC
    self.src = self.conn
    self.history = []
  
  class Connection:
    def __init__(self, db):
      self.db = db
    def cursor(self): return self
    def close(self): pass
    def execute(self, sql, args):
      self.db.history.append((sql, args))
    def fetchmany(self):
      return []
  
  def conn(self):  
    return EchoDatabase.Connection(self)
    
  

class GenericDialect(object):

  KEYWORDS = set('''
    ADD ADD CONSTRAINT ALTER ALTER COLUMN ALTER TABLE ALL AND ANY AS ASC BETWEEN CASE CHECK COLUMN CONSTRAINT CREATE CREATE DATABASE CREATE INDEX CREATE OR REPLACE VIEW CREATE TABLE CREATE 
    PROCEDURE CREATE UNIQUE INDEX CREATE VIEW DATABASE DEFAULT DELETE DESC DISTINCT DROP DROP COLUMN DROP CONSTRAINT DROP DATABASE DROP DEFAULT DROP INDEX DROP TABLE DROP VIEW EXEC EXISTS 
    FOREIGN KEY FROM FULL OUTER JOIN GROUP BY HAVING IN INDEX INNER JOIN INSERT INTO INSERT INTO SELECT IS NULL IS NOT NULL JOIN LEFT JOIN LIKE LIMIT NOT NOT NULL OR ORDER BY OUTER JOIN 
    PRIMARY KEY PROCEDURE RIGHT JOIN ROWNUM SELECT SELECT DISTINCT SELECT INTO SELECT TOP SET TABLE TOP TRUNCATE TABLE UNION UNION ALL UNIQUE UPDATE VALUES VIEW WHERE
  '''.lower().split())

  def __init__(self):
    self.version = None
    self.lib = None
  
  def __call__(self, version=None, lib=None):
    self = copy.copy(self)
    self.version = version or self.version
    if isinstance(lib, types.ModuleType):
      lib = lib.__name__
    self.lib = lib or self.lib
    return self
  
  def term(self, s):
    s = s.lower().replace('"','')
    if s not in self.KEYWORDS: return s
    else: return '"%s"' % s

  @property
  def arg(self):
    self.arg_counter += 1
    if self.lib=='psycopg2': return '%s'
    elif self.lib=='asyncpg': return '$%i' % self.arg_counter
    else: return '?'
  
  def for_query(self):
    self = copy.copy(self)
    self.arg_counter = 0
    return self
  
  

class PostgresDialect(GenericDialect):
  KEYWORDS = set('''
    A ABORT ABS ABSENT ABSOLUTE ACCESS ACCORDING ACTION ADA ADD ADMIN AFTER AGGREGATE ALL ALLOCATE ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARE ARRAY ARRAY_AGG ARRAY_MAX_CARDINALITY AS ASC 
    ASENSITIVE ASSERTION ASSIGNMENT ASYMMETRIC AT ATOMIC ATTRIBUTE ATTRIBUTES AUTHORIZATION AVG BACKWARD BASE64 BEFORE BEGIN BEGIN_FRAME BEGIN_PARTITION BERNOULLI BETWEEN BIGINT BINARY BIT 
    BIT_LENGTH BLOB BLOCKED BOM BOOLEAN BOTH BREADTH BY C CACHE CALL CALLED CARDINALITY CASCADE CASCADED CASE CAST CATALOG CATALOG_NAME CEIL CEILING CHAIN CHAR CHARACTER CHARACTERISTICS 
    CHARACTERS CHARACTER_LENGTH CHARACTER_SET_CATALOG CHARACTER_SET_NAME CHARACTER_SET_SCHEMA CHAR_LENGTH CHECK CHECKPOINT CLASS CLASS_ORIGIN CLOB CLOSE CLUSTER COALESCE COBOL COLLATE COLLATION 
    COLLATION_CATALOG COLLATION_NAME COLLATION_SCHEMA COLLECT COLUMN COLUMNS COLUMN_NAME COMMAND_FUNCTION COMMAND_FUNCTION_CODE COMMENT COMMENTS COMMIT COMMITTED CONCURRENTLY CONDITION 
    CONDITION_NUMBER CONFIGURATION CONFLICT CONNECT CONNECTION CONNECTION_NAME CONSTRAINT CONSTRAINTS CONSTRAINT_CATALOG CONSTRAINT_NAME CONSTRAINT_SCHEMA CONSTRUCTOR CONTAINS CONTENT CONTINUE 
    CONTROL CONVERSION CONVERT COPY CORR CORRESPONDING COST COUNT COVAR_POP COVAR_SAMP CREATE CROSS CSV CUBE CUME_DIST CURRENT CURRENT_CATALOG CURRENT_DATE CURRENT_DEFAULT_TRANSFORM_GROUP 
    CURRENT_PATH CURRENT_ROLE CURRENT_ROW CURRENT_SCHEMA CURRENT_TIME CURRENT_TIMESTAMP CURRENT_TRANSFORM_GROUP_FOR_TYPE CURRENT_USER CURSOR CURSOR_NAME CYCLE DATA DATABASE DATALINK DATE 
    DATETIME_INTERVAL_CODE DATETIME_INTERVAL_PRECISION DAY DB DEALLOCATE DEC DECIMAL DECLARE DEFAULT DEFAULTS DEFERRABLE DEFERRED DEFINED DEFINER DEGREE DELETE DELIMITER DELIMITERS DENSE_RANK 
    DEPENDS DEPTH DEREF DERIVED DESC DESCRIBE DESCRIPTOR DETERMINISTIC DIAGNOSTICS DICTIONARY DISABLE DISCARD DISCONNECT DISPATCH DISTINCT DLNEWCOPY DLPREVIOUSCOPY DLURLCOMPLETE 
    DLURLCOMPLETEONLY DLURLCOMPLETEWRITE DLURLPATH DLURLPATHONLY DLURLPATHWRITE DLURLSCHEME DLURLSERVER DLVALUE DO DOCUMENT DOMAIN DOUBLE DROP DYNAMIC DYNAMIC_FUNCTION DYNAMIC_FUNCTION_CODE 
    EACH ELEMENT ELSE EMPTY ENABLE ENCODING ENCRYPTED END END-EXEC END_FRAME END_PARTITION ENFORCED ENUM EQUALS ESCAPE EVENT EVERY EXCEPT EXCEPTION EXCLUDE EXCLUDING EXCLUSIVE EXEC EXECUTE 
    EXISTS EXP EXPLAIN EXPRESSION EXTENSION EXTERNAL EXTRACT FALSE FAMILY FETCH FILE FILTER FINAL FIRST FIRST_VALUE FLAG FLOAT FLOOR FOLLOWING FOR FORCE FOREIGN FORTRAN FORWARD FOUND FRAME_ROW 
    FREE FREEZE FROM FS FULL FUNCTION FUNCTIONS FUSION G GENERAL GENERATED GET GLOBAL GO GOTO GRANT GRANTED GREATEST GROUP GROUPING GROUPS HANDLER HAVING HEADER HEX HIERARCHY HOLD HOUR ID 
    IDENTITY IF IGNORE ILIKE IMMEDIATE IMMEDIATELY IMMUTABLE IMPLEMENTATION IMPLICIT IMPORT IN INCLUDING INCREMENT INDENT INDEX INDEXES INDICATOR INHERIT INHERITS INITIALLY INLINE INNER INOUT 
    INPUT INSENSITIVE INSERT INSTANCE INSTANTIABLE INSTEAD INT INTEGER INTEGRITY INTERSECT INTERSECTION INTERVAL INTO INVOKER IS ISNULL ISOLATION JOIN K KEY KEY_MEMBER KEY_TYPE LABEL LAG 
    LANGUAGE LARGE LAST LAST_VALUE LATERAL LEAD LEADING LEAKPROOF LEAST LEFT LENGTH LEVEL LIBRARY LIKE LIKE_REGEX LIMIT LINK LISTEN LN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCATOR LOCK 
    LOCKED LOGGED LOWER M MAP MAPPING MATCH MATCHED MATERIALIZED MAX MAXVALUE MAX_CARDINALITY MEMBER MERGE MESSAGE_LENGTH MESSAGE_OCTET_LENGTH MESSAGE_TEXT METHOD MIN MINUTE MINVALUE MOD MODE 
    MODIFIES MODULE MONTH MORE MOVE MULTISET MUMPS NAME NAMES NAMESPACE NATIONAL NATURAL NCHAR NCLOB NESTING NEW NEXT NFC NFD NFKC NFKD NIL NO NONE NORMALIZE NORMALIZED NOT NOTHING NOTIFY 
    NOTNULL NOWAIT NTH_VALUE NTILE NULL NULLABLE NULLIF NULLS NUMBER NUMERIC OBJECT OCCURRENCES_REGEX OCTETS OCTET_LENGTH OF OFF OFFSET OIDS OLD ON ONLY OPEN OPERATOR OPTION OPTIONS OR ORDER 
    ORDERING ORDINALITY OTHERS OUT OUTER OUTPUT OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER P PAD PARALLEL PARAMETER PARAMETER_MODE PARAMETER_NAME PARAMETER_ORDINAL_POSITION 
    PARAMETER_SPECIFIC_CATALOG PARAMETER_SPECIFIC_NAME PARAMETER_SPECIFIC_SCHEMA PARSER PARTIAL PARTITION PASCAL PASSING PASSTHROUGH PASSWORD PATH PERCENT PERCENTILE_CONT PERCENTILE_DISC 
    PERCENT_RANK PERIOD PERMISSION PLACING PLANS PLI POLICY PORTION POSITION POSITION_REGEX POWER PRECEDES PRECEDING PRECISION PREPARE PREPARED PRESERVE PRIMARY PRIOR PRIVILEGES PROCEDURAL 
    PROCEDURE PROGRAM PUBLIC QUOTE RANGE RANK READ READS REAL REASSIGN RECHECK RECOVERY RECURSIVE REF REFERENCES REFERENCING REFRESH REGR_AVGX REGR_AVGY REGR_COUNT REGR_INTERCEPT REGR_R2 
    REGR_SLOPE REGR_SXX REGR_SXY REGR_SYY REINDEX RELATIVE RELEASE RENAME REPEATABLE REPLACE REPLICA REQUIRING RESET RESPECT RESTART RESTORE RESTRICT RESULT RETURN RETURNED_CARDINALITY 
    RETURNED_LENGTH RETURNED_OCTET_LENGTH RETURNED_SQLSTATE RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP ROUTINE ROUTINE_CATALOG ROUTINE_NAME ROUTINE_SCHEMA ROW ROWS ROW_COUNT 
    ROW_NUMBER RULE SAVEPOINT SCALE SCHEMA SCHEMA_NAME SCOPE SCOPE_CATALOG SCOPE_NAME SCOPE_SCHEMA SCROLL SEARCH SECOND SECTION SECURITY SELECT SELECTIVE SELF SENSITIVE SEQUENCE SEQUENCES 
    SERIALIZABLE SERVER SERVER_NAME SESSION SESSION_USER SET SETOF SETS SHARE SHOW SIMILAR SIMPLE SIZE SKIP SMALLINT SNAPSHOT SOME SOURCE SPACE SPECIFIC SPECIFICTYPE SPECIFIC_NAME SQL SQLCODE 
    SQLERROR SQLEXCEPTION SQLSTATE SQLWARNING SQRT STABLE STANDALONE START STATE STATEMENT STATIC STATISTICS STDDEV_POP STDDEV_SAMP STDIN STDOUT STORAGE STRICT STRIP STRUCTURE STYLE 
    SUBCLASS_ORIGIN SUBMULTISET SUBSTRING SUBSTRING_REGEX SUCCEEDS SUM SYMMETRIC SYSID SYSTEM SYSTEM_TIME SYSTEM_USER T TABLE TABLES TABLESAMPLE TABLESPACE TABLE_NAME TEMP TEMPLATE TEMPORARY 
    TEXT THEN TIES TIME TIMESTAMP TIMEZONE_HOUR TIMEZONE_MINUTE TO TOKEN TOP_LEVEL_COUNT TRAILING TRANSACTION TRANSACTIONS_COMMITTED TRANSACTIONS_ROLLED_BACK TRANSACTION_ACTIVE TRANSFORM 
    TRANSFORMS TRANSLATE TRANSLATE_REGEX TRANSLATION TREAT TRIGGER TRIGGER_CATALOG TRIGGER_NAME TRIGGER_SCHEMA TRIM TRIM_ARRAY TRUE TRUNCATE TRUSTED TYPE TYPES UESCAPE UNBOUNDED UNCOMMITTED 
    UNDER UNENCRYPTED UNION UNIQUE UNKNOWN UNLINK UNLISTEN UNLOGGED UNNAMED UNNEST UNTIL UNTYPED UPDATE UPPER URI USAGE USER USER_DEFINED_TYPE_CATALOG USER_DEFINED_TYPE_CODE 
    USER_DEFINED_TYPE_NAME USER_DEFINED_TYPE_SCHEMA USING VACUUM VALID VALIDATE VALIDATOR VALUE VALUES VALUE_OF VARBINARY VARCHAR VARIADIC VARYING VAR_POP VAR_SAMP VERBOSE VERSION VERSIONING
    VIEW VIEWS VOLATILE WHEN WHENEVER WHERE WHITESPACE WIDTH_BUCKET WINDOW WITH WITHIN WITHOUT WORK WRAPPER WRITE XML XMLAGG XMLATTRIBUTES XMLBINARY XMLCAST XMLCOMMENT XMLCONCAT XMLDECLARATION 
    XMLDOCUMENT XMLELEMENT XMLEXISTS XMLFOREST XMLITERATE XMLNAMESPACES XMLPARSE XMLPI XMLQUERY XMLROOT XMLSCHEMA XMLSERIALIZE XMLTABLE XMLTEXT XMLVALIDATE YEAR YES ZONE
  '''.lower().split())
    
    
    
class Dialect(object):
  GENERIC = GenericDialect()
  POSTGRES = PostgresDialect()
  

