text: # <pep8-80 compliant>
  
  # ##### BEGIN GPL LICENSE BLOCK #####
  #
  #  This program is free software; you can redistribute it and/or
  #  modify it under the terms of the GNU General Public License
  #  as published by the Free Software Foundation; either version 2
  #  of the License, or (at your option) any later version.
  #
  #  This program is distributed in the hope that it will be useful,
  #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  #  GNU General Public License for more details.
  #
  #  You should have received a copy of the GNU General Public License
  #  along with this program; if not, write to the Free Software Foundation,
  #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  #
  # ##### END GPL LICENSE BLOCK #####
  
  __author__ = "Nutti <nutti.metro@gmail.com>"
  __status__ = "production"
  __version__ = "6.2"
  __date__ = "31 Jul 2019"
  
  import bpy
  from bpy.props import (
      BoolProperty,
  )
  import bmesh
  
  from .. import common
  from ..utils.bl_class_registry import BlClassRegistry
  from ..utils.property_class_registry import PropertyClassRegistry
  
  
  def _is_valid_context(context):
      obj = context.object
  
      # only edit mode is allowed to execute
      if obj is None:
          return False
      if obj.type != 'MESH':
          return False
      if context.object.mode != 'EDIT':
          return False
  
      # only 'VIEW_3D' space is allowed to execute
      for space in context.area.spaces:
          if space.type == 'VIEW_3D':
              break
      else:
          return False
  
      return True
  
  
  @PropertyClassRegistry()
  class _Properties:
      idname = "texture_wrap"
  
      @classmethod
      def init_props(cls, scene):
          class Props():
              ref_face_index = -1
              ref_obj = None
  
          scene.muv_props.texture_wrap = Props()
  
          scene.muv_texture_wrap_enabled = BoolProperty(
              name="Texture Wrap",
              description="Texture Wrap is enabled",
              default=False
          )
          scene.muv_texture_wrap_set_and_refer = BoolProperty(
              name="Set and Refer",
              description="Refer and set UV",
              default=True
          )
          scene.muv_texture_wrap_selseq = BoolProperty(
              name="Selection Sequence",
              description="Set UV sequentially",
              default=False
          )
  
      @classmethod
      def del_props(cls, scene):
          del scene.muv_props.texture_wrap
          del scene.muv_texture_wrap_enabled
          del scene.muv_texture_wrap_set_and_refer
          del scene.muv_texture_wrap_selseq
  
  
  @BlClassRegistry()
  class MUV_OT_TextureWrap_Refer(bpy.types.Operator):
      """
      Operation class: Refer UV
      """
  
      bl_idname = "uv.muv_texture_wrap_refer"
      bl_label = "Refer"
      bl_description = "Refer UV"
      bl_options = {'REGISTER', 'UNDO'}
  
      @classmethod
      def poll(cls, context):
          # we can not get area/space/region from console
          if common.is_console_mode():
              return True
          return _is_valid_context(context)
  
      def execute(self, context):
          props = context.scene.muv_props.texture_wrap
          obj = context.active_object
          bm = bmesh.from_edit_mesh(obj.data)
          if common.check_version(2, 73, 0) >= 0:
              bm.faces.ensure_lookup_table()
  
          if not bm.loops.layers.uv:
              self.report({'WARNING'}, "Object must have more than one UV map")
              return {'CANCELLED'}
  
          sel_faces = [f for f in bm.faces if f.select]
          if len(sel_faces) != 1:
              self.report({'WARNING'}, "Must select only one face")
              return {'CANCELLED'}
  
          props.ref_face_index = sel_faces[0].index
          props.ref_obj = obj
  
          return {'FINISHED'}
  
  
  @BlClassRegistry()
  class MUV_OT_TextureWrap_Set(bpy.types.Operator):
      """
      Operation class: Set UV
      """
  
      bl_idname = "uv.muv_texture_wrap_set"
      bl_label = "Set"
      bl_description = "Set UV"
      bl_options = {'REGISTER', 'UNDO'}
  
      @classmethod
      def poll(cls, context):
          # we can not get area/space/region from console
          if common.is_console_mode():
              return True
          sc = context.scene
          props = sc.muv_props.texture_wrap
          if not props.ref_obj:
              return False
          return _is_valid_context(context)
  
      def execute(self, context):
          sc = context.scene
          props = sc.muv_props.texture_wrap
          obj = context.active_object
          bm = bmesh.from_edit_mesh(obj.data)
          if common.check_version(2, 73, 0) >= 0:
              bm.faces.ensure_lookup_table()
  
          if not bm.loops.layers.uv:
              self.report({'WARNING'}, "Object must have more than one UV map")
              return {'CANCELLED'}
          uv_layer = bm.loops.layers.uv.verify()
  
          if sc.muv_texture_wrap_selseq:
              sel_faces = []
              for hist in bm.select_history:
                  if isinstance(hist, bmesh.types.BMFace) and hist.select:
                      sel_faces.append(hist)
              if not sel_faces:
                  self.report({'WARNING'}, "Must select more than one face")
                  return {'CANCELLED'}
          else:
              sel_faces = [f for f in bm.faces if f.select]
              if len(sel_faces) != 1:
                  self.report({'WARNING'}, "Must select only one face")
                  return {'CANCELLED'}
  
          ref_face_index = props.ref_face_index
          for face in sel_faces:
              tgt_face_index = face.index
              if ref_face_index == tgt_face_index:
                  self.report({'WARNING'}, "Must select different face")
                  return {'CANCELLED'}
  
              if props.ref_obj != obj:
                  self.report({'WARNING'}, "Object must be same")
                  return {'CANCELLED'}
  
              ref_face = bm.faces[ref_face_index]
              tgt_face = bm.faces[tgt_face_index]
  
              # get common vertices info
              common_verts = []
              for sl in ref_face.loops:
                  for dl in tgt_face.loops:
                      if sl.vert == dl.vert:
                          info = {"vert": sl.vert, "ref_loop": sl,
                                  "tgt_loop": dl}
                          common_verts.append(info)
                          break
  
              if len(common_verts) != 2:
                  self.report({'WARNING'},
                              "2 vertices must be shared among faces")
                  return {'CANCELLED'}
  
              # get reference other vertices info
              ref_other_verts = []
              for sl in ref_face.loops:
                  for ci in common_verts:
                      if sl.vert == ci["vert"]:
                          break
                  else:
                      info = {"vert": sl.vert, "loop": sl}
                      ref_other_verts.append(info)
  
              if not ref_other_verts:
                  self.report({'WARNING'}, "More than 1 vertex must be unshared")
                  return {'CANCELLED'}
  
              # get reference info
              ref_info = {}
              cv0 = common_verts[0]["vert"].co
              cv1 = common_verts[1]["vert"].co
              cuv0 = common_verts[0]["ref_loop"][uv_layer].uv
              cuv1 = common_verts[1]["ref_loop"][uv_layer].uv
              ov0 = ref_other_verts[0]["vert"].co
              ouv0 = ref_other_verts[0]["loop"][uv_layer].uv
              ref_info["vert_vdiff"] = cv1 - cv0
              ref_info["uv_vdiff"] = cuv1 - cuv0
              ref_info["vert_hdiff"], _ = common.diff_point_to_segment(
                  cv0, cv1, ov0)
              ref_info["uv_hdiff"], _ = common.diff_point_to_segment(
                  cuv0, cuv1, ouv0)
  
              # get target other vertices info
              tgt_other_verts = []
              for dl in tgt_face.loops:
                  for ci in common_verts:
                      if dl.vert == ci["vert"]:
                          break
                  else:
                      info = {"vert": dl.vert, "loop": dl}
                      tgt_other_verts.append(info)
  
              if not tgt_other_verts:
                  self.report({'WARNING'}, "More than 1 vertex must be unshared")
                  return {'CANCELLED'}
  
              # get target info
              for info in tgt_other_verts:
                  cv0 = common_verts[0]["vert"].co
                  cv1 = common_verts[1]["vert"].co
                  cuv0 = common_verts[0]["ref_loop"][uv_layer].uv
                  ov = info["vert"].co
                  info["vert_hdiff"], x = common.diff_point_to_segment(
                      cv0, cv1, ov)
                  info["vert_vdiff"] = x - common_verts[0]["vert"].co
  
                  # calclulate factor
                  fact_h = -info["vert_hdiff"].length / \
                      ref_info["vert_hdiff"].length
                  fact_v = info["vert_vdiff"].length / \
                      ref_info["vert_vdiff"].length
                  duv_h = ref_info["uv_hdiff"] * fact_h
                  duv_v = ref_info["uv_vdiff"] * fact_v
  
                  # get target UV
                  info["target_uv"] = cuv0 + duv_h + duv_v
  
              # apply to common UVs
              for info in common_verts:
                  info["tgt_loop"][uv_layer].uv = \
                      info["ref_loop"][uv_layer].uv.copy()
              # apply to other UVs
              for info in tgt_other_verts:
                  info["loop"][uv_layer].uv = info["target_uv"]
  
              common.debug_print("===== Target Other Vertices =====")
              common.debug_print(tgt_other_verts)
  
              bmesh.update_edit_mesh(obj.data)
  
              ref_face_index = tgt_face_index
  
          if sc.muv_texture_wrap_set_and_refer:
              props.ref_face_index = tgt_face_index
  
          return {'FINISHED'}
  