"""Node implementation for the Toron project."""

import os
from contextlib import closing
from itertools import compress

from ._node_schema import connect
from ._node_schema import savepoint
from ._node_schema import _get_column_names
from ._node_schema import _make_sql_new_labels
from ._node_schema import _make_sql_insert_elements
from ._node_schema import _insert_weight_get_id
from ._node_schema import _make_sql_insert_element_weight
from ._node_schema import _update_weight_is_complete


class Node(object):
    def __init__(self, path, mode='rwc'):
        path = os.fspath(path)
        connect(path, mode=mode).close()  # Verify path to Toron node file.
        self._path = path
        self.mode = mode

    @property
    def path(self):
        return self._path

    def add_columns(self, columns):
        with closing(connect(self.path, mode=self.mode)) as con:
            with closing(con.cursor()) as cur:
                with savepoint(cur):
                    for stmnt in _make_sql_new_labels(cur, columns):
                        cur.execute(stmnt)

    def add_elements(self, iterable, columns=None):
        iterator = iter(iterable)
        if not columns:
            columns = next(iterator)

        with closing(connect(self.path, mode=self.mode)) as con:
            with closing(con.cursor()) as cur:
                with savepoint(cur):
                    # Get allowed columns and build selectors values.
                    allowed_columns = _get_column_names(cur, 'element')
                    selectors = tuple((col in allowed_columns) for col in columns)

                    # Filter column names and iterator rows to allowed columns.
                    columns = compress(columns, selectors)
                    iterator = (tuple(compress(row, selectors)) for row in iterator)

                    sql = _make_sql_insert_elements(cur, columns)
                    cur.executemany(sql, iterator)

    def add_weights(self, iterable, columns=None, *, name, type_info, description=None):
        iterator = iter(iterable)
        if not columns:
            columns = tuple(next(iterator))

        try:
            weight_pos = columns.index(name)  # Get position of weight column.
        except ValueError:
            columns_string = ', '.join(repr(x) for x in columns)
            msg = f'Name {name!r} does not appear in columns: {columns_string}'
            raise ValueError(msg)

        with closing(connect(self.path, mode=self.mode)) as con:
            with closing(con.cursor()) as cur:
                with savepoint(cur):
                    weight_id = _insert_weight_get_id(cur, name, type_info, description)

                    # Get allowed columns and build selectors values.
                    allowed_columns = _get_column_names(cur, 'element')
                    selectors = tuple((col in allowed_columns) for col in columns)

                    # Filter column names and iterator rows to allowed columns.
                    columns = compress(columns, selectors)
                    def mkrow(row):
                        weightid_and_value = (weight_id, row[weight_pos])
                        element_labels = tuple(compress(row, selectors))
                        return weightid_and_value + element_labels
                    iterator = (mkrow(row) for row in iterator)

                    # Insert element_weight records.
                    sql = _make_sql_insert_element_weight(cur, columns)
                    cur.executemany(sql, iterator)

                    # Update "weight.is_complete" value (set to 1 or 0).
                    _update_weight_is_complete(cur, weight_id)


@@ -176,25 +176,27 @@
  import 
-savepoint
+transaction
 %0Afrom ._
@@ -711,16 +711,91 @@
 e = mode
+%0A        self._transaction = lambda: transaction(self.path, mode=self.mode)
 %0A%0A    @p
@@ -902,152 +902,36 @@
 ith 
-closing(connect(self.path, mode=self.mode)) as con:%0A            with closing(con.cursor()) as cur:%0A                with savepoint(cur):%0A        
+self._transaction() as cur:%0A
     
@@ -987,24 +987,16 @@
 lumns):%0A
-        
         
@@ -1188,152 +1188,36 @@
 ith 
-closing(connect(self.path, mode=self.mode)) as con:%0A            with closing(con.cursor()) as cur:%0A                with savepoint(cur):%0A        
+self._transaction() as cur:%0A
     
@@ -1282,32 +1282,24 @@
             
-     
-   
 allowed_colu
@@ -1334,32 +1334,24 @@
  'element')%0A
-        
             
@@ -1422,32 +1422,24 @@
             
-      
-  
 # Filter col
@@ -1494,32 +1494,24 @@
             
-        
 columns = co
@@ -1545,32 +1545,24 @@
             
-       
- 
 iterator = (
@@ -1623,32 +1623,24 @@
             
-        
 sql = _make_
@@ -1669,32 +1669,24 @@
 r, columns)%0A
-        
             
@@ -2225,152 +2225,36 @@
 ith 
-closing(connect(self.path, mode=self.mode)) as con:%0A            with closing(con.cursor()) as cur:%0A                with savepoint(cur):%0A        
+self._transaction() as cur:%0A
     
@@ -2339,32 +2339,24 @@
             
-        
 # Get allowe
@@ -2389,32 +2389,24 @@
 ors values.%0A
-        
             
@@ -2465,32 +2465,24 @@
             
-        
 selectors = 
@@ -2541,32 +2541,24 @@
             
- 
-       
 # Filter col
@@ -2613,32 +2613,24 @@
             
-  
-      
 columns = co
@@ -2668,24 +2668,16 @@
         
-        
 def mkro
@@ -2696,32 +2696,24 @@
             
-   
-     
 weightid_and
@@ -2766,24 +2766,16 @@
         
-    
-    
 element_
@@ -2831,24 +2831,16 @@
         
-     
-   
 return w
@@ -2882,32 +2882,24 @@
             
-      
-  
 iterator = (
@@ -2943,24 +2943,16 @@
         
-      
-  
 # Insert
@@ -2976,24 +2976,16 @@
 ecords.%0A
-        
         
@@ -3048,32 +3048,24 @@
             
-       
- 
 cur.executem
@@ -3080,32 +3080,24 @@
  iterator)%0A%0A
-        
             
@@ -3149,24 +3149,16 @@
  or 0).%0A
-        
         

Change Node methods to use transaction() context manager.