# coding: utf-8
from __future__ import unicode_literals

import traceback


def get_error(title, *args, **kwargs):
    template = '\n\n\t\033[91m\033[1m{title}\033[0m\n{info}{tb}\n'
    info = '\n'.join(['\t' + arg for arg in args])
    tb = _get_traceback(kwargs['tb']) if 'tb' in kwargs else ''
    return template.format(title=title, info=info, tb=tb).encode('utf8')


def _get_traceback(tb):
    template = '\n\n\t\033[94m\033[1m{title}:\033[0m\n\t{tb}'
    tb_range = tb[-5:-2]
    tb_list = [_format_traceback(p, l, fn, t, i, len(tb_range)) for i, (p, l, fn, t) in enumerate(tb_range)]
    tb_str = '\n'.join(tb_list).strip()
    return template.format(title='Traceback', tb=tb_str)


def _format_traceback(path, line, fn, text, i, count):
    template = '\t{i} \033[1m{fn}\033[0m [{l}] in \033[4m{p}\033[0m{t}'
    template_text = '\n\t{sp} \033[91m>>>\033[0m {t}'
    indent = ('└─' if i == count-1 else '├─') + '──'*i
    filename = path.rsplit('/thinc/', 1)[1] if '/thinc/' in path else path
    text = template_text.format(sp='   '*i, t=text) if i == count-1 else ''
    return template.format(l=str(line), fn=fn, p=filename, i=indent, t=text)


class UndefinedOperatorError(TypeError):
    def __init__(self, op, arg1, arg2, operators):
        self.tb = traceback.extract_stack()
        msg = get_error(
            "Undefined operator: {op}".format(op=op),
            "Called by ({arg1}, {arg2})".format(arg1=arg1, arg2=arg2),
            "Available: {ops}".format(ops= ', '.join(operators.keys())),
            tb=self.tb
        )

        TypeError.__init__(self, msg)




# SHAPE_ERR_TEMPLATE = '''

# In the context of {context}:\n

# Shape1 != Shape2

# Where:

# Shape1={shape1}
# Shape2={shape2}
# '''

# class ShapeError(ValueError):
#     def __init__(self, shape1, shape2, context):
#         msg = SHAPE_ERR_TEMPLATE.strip().format(
#             shape1=shape1, shape2=shape2, context=context)
#         ValueError.__init__(self, msg)
#         self.tb = sys.exc_info()[2]

#     @classmethod
#     def dimensions_mismatch(cls, shape1, shape2, context):
#         if shape1 == shape2:
#             return None
#         else:
#             return cls(shape1, shape2, context)

#     @classmethod
#     def dim_mismatch(cls, expected, observed):
#         return cls("Dimension mismatch: %s vs %s" % (expected, observed))

@@ -159,18 +159,16 @@
 %7D%5C033%5B0m
-%5Cn
 %7Binfo%7D%7Bt
@@ -183,16 +183,23 @@
   info =
+ '%5Cn' +
  '%5Cn'.jo
@@ -230,16 +230,32 @@
 n args%5D)
+ if args else ''
 %0A    tb 
@@ -1608,168 +1608,34 @@
 )%0A%0A%0A
-%0A%0A# SHAPE_ERR_TEMPLATE = '''%0A%0A# In the context of %7Bcontext%7D:%5Cn%0A%0A# Shape1 != Shape2%0A%0A# Where:%0A%0A# Shape1=%7Bshape1%7D%0A# Shape2=%7Bshape2%7D%0A# '''%0A%0A# class Shape
+class ExpectedInt
 Error(
-Valu
+Typ
 eErr
@@ -1639,18 +1639,16 @@
 Error):%0A
-# 
     def 
@@ -1666,43 +1666,25 @@
 lf, 
-shape1, shape2, contex
+no_in
 t):%0A
-#
         
  msg
@@ -1683,120 +1683,52 @@
     
- msg = SHAPE_ERR_TEMPLATE.strip().format(%0A#             shape1=shape1, shape2=shape2, context=context
+self.tb = traceback.extract_stack(
 )%0A
-#
         
  Val
@@ -1727,423 +1727,169 @@
     
- ValueError.__init__(self, msg)%0A#         self.tb = sys.exc_info()%5B2%5D%0A%0A#     @classmethod%0A#     def dimensions_mismatch(cls, shape1, shape2, context):%0A#         if shape1 == shape2:%0A#             return None%0A#         else:%0A#             return cls(shape1, shape2, context
+msg = get_error(%0A            %22Expected an integer, but got: %7Bno_int%7D%22.format(no_int=no_int),%0A            tb=self.tb%0A        
 )%0A%0A
-#
      
-@classmethod%0A#     def dim_mismatch(cls, expected, observed):%0A#         return cls(%22Dimension mismatch: %25s vs %25s%22 %25 (expected, observed)
+   TypeError.__init__(self, msg
 )%0A

Update exceptions and add ExpectedIntError class