 drawObjects = (instances, messages) ->
   canvas = document.getElementById("diagram")
   return unless canvas
+  ctx = canvas.getContext("2d")
+  ctx.font = "12px Sans-Serif"
+  addRectToInstances(ctx, instances, messages)
+  addRectToMessages(ctx, instances, messages)
   canvas.height = calcCanvasHeight(messages)
-  canvas.width = calcCanvasWidth(instances)
-  ctx = canvas.getContext("2d")
+  ctx.font = "12px Sans-Serif"
+  canvas.width = calcCanvasWidth(ctx, instances)
   fillBackground(ctx, canvas.width, canvas.height)
   drawInstances(ctx, instances, canvas.height - MESSSAGE_OFFSET - CANVAS_PADDING)
   drawMessages(ctx, messages)
 
+addRectToInstances = (ctx, instances, messages) ->
+  first = _.min(instances, (obj) -> obj.order)
+  x = CANVAS_PADDING + getInstanceMargin(ctx, instances, messages, first)
+  for obj in instances
+    obj.rect = createInstanceRect(ctx, x, obj)
+    x += Math.round(obj.rect.width + INSTANCE_OFFSET)
+
+getInstanceMargin = (ctx, instances, messages, instance) ->
+  return 0 unless instance?
+  
+  # TODO noteBodyMargin
+  normalBodyMargin = getMaxNormalBodyMargin(ctx, instance)
+  selfBodyMargin = getMaxSelfBodyMargin(ctx, instance)
+  bodyMargin = Math.max(normalBodyMargin, selfBodyMargin)
+  
+  nameWidth1 = roundInstanceWidth(ctx, instance.name)
+  nameMargin = nameWidth1 / 2
+  gap = Math.round(bodyMargin - nameMargin)
+  return gap if gap > 0
+  
+  return 0
+
+getMaxSelfBodyMargin = (ctx, instance) ->
+  message = _.max(messages, (obj) -> getRelatedSelfBodyWidth(ctx, instance, obj))
+  return 0 unless isValidMessage(message)
+  return 0 unless isSelfMessage(message)
+  return 0 if isNote(message)
+  return 0 unless message.from is instance
+  
+  bodyWidth = ctx.measureText(message.body).width
+  return bodyWidth / 2
+
+getRelatedSelfBodyWidth = (ctx, instance, message) ->
+  return 0 unless message.from is instance
+  return 0 unless isSelfMessage(message)
+  
+  bodyWidth = ctx.measureText(message.body).width
+  return bodyWidth / 2
+
+getMaxNormalBodyMargin = (ctx, instance1) ->
+  message = _.max(messages, (obj) -> getRelatedBodyWidth(ctx, instance1, obj))
+  return 0 unless isValidMessage(message)
+  
+  instance2 = message.to
+  return 0 unless instance2?
+  
+  nameWidth1 = roundInstanceWidth(ctx, instance1.name)
+  nameWidth2 = roundInstanceWidth(ctx, instance2.name)
+  
+  distance = nameWidth1 / 2 + nameWidth2 / 2 + INSTANCE_OFFSET
+  bodyWidth = ctx.measureText(message.body).width
+  return bodyWidth / 2 - distance / 2
+
+getRelatedBodyWidth = (ctx, instance1, message) ->
+  return 0 if isSelfMessage(message)
+  
+  if message.from is instance1
+    instance2 = message.to
+  else if message.to is instance1
+    instance2 = message.from
+  else
+    return 0
+  return 0 unless instance2?
+  
+  nameWidth1 = roundInstanceWidth(ctx, instance1.name)
+  nameWidth2 = roundInstanceWidth(ctx, instance2.name)
+  
+  distance = nameWidth1 / 2 + nameWidth2 / 2 + INSTANCE_OFFSET
+  bodyWidth = ctx.measureText(message.body).width
+  return bodyWidth
+
+createInstanceRect = (ctx, x, obj) ->
+  rect = new Object()
+  rect.x = x
+  rect.y = CANVAS_PADDING
+  rect.width = roundInstanceWidth(ctx, obj.name)
+  rect.height = INSTANCE_HEIGHT
+  rect.centerX = Math.round(rect.x + rect.width / 2)
+  rect.centerY = Math.round(rect.y + rect.height / 2)
+  return rect
+
+roundInstanceWidth = (ctx, name) ->
+  nameWidth = ctx.measureText(name).width
+  return Math.max(INSTANCE_WIDTH, nameWidth + INSTANCE_PADDING)
+
+addRectToMessages = (ctx, instances, messages) ->
+  for obj in messages
+    if checkPresenceOfToFromObjects(obj)
+      obj.rect = createMessageRect(ctx, instances, obj)
+      if isNote(obj)
+        obj.note = createNote(obj.rect)
+      else if isSelfMessage(obj)
+        obj.selfMessage = createSelfMessage(ctx, instances, obj)
+      else
+        obj.normalMessage = createNormalMessage(instances, obj)
+
+createMessageRect = (ctx, instances, obj) ->
+  rect = new Object()
+  pt = createIntersectionPoint(instances, obj.from.order, obj.order)
+  rect.x1 = pt.x
+  rect.y1 = pt.y
+  pt = createIntersectionPoint(instances, obj.to.order, obj.order)
+  rect.x2 = pt.x
+  rect.y2 = pt.y
+  rect.centerX = getMiddlePos(rect.x1, rect.x2)
+  rect.centerY = getMiddlePos(rect.y1, rect.y2)
+  obj.rect = rect
+
+createIntersectionPoint = (instances, instanceOrder, messageOrder) ->
+  result = new Object()
+  rect = getInstanceRectByOrder(instances, instanceOrder)
+  result.x = rect.centerX
+  result.y = rect.y + rect.height + (messageOrder + 1) * MESSSAGE_OFFSET
+  return result
+
+getMiddlePos = (a, b) ->
+  return a + (b - a) / 2
+
+createNote = (rect) ->
+  pt = new Object()
+  pt.x = rect.centerX - INSTANCE_WIDTH / 2
+  pt.y = rect.centerY
+  note = new Object()
+  note.bodyPoint = pt
+  return note
+
+createSelfMessage = (ctx, instances, obj) ->
+  result = new Object()
+  result.rect = createSelfMessageRect(ctx, instances, obj)
+  result.headPoint = createSelfMessageHeadPoint(result.rect)
+  result.bodyPoint = createSelfMessageBodyPoint(result.rect)
+  return result
+
+createSelfMessageRect = (ctx, instances, obj) ->
+  result = new Object()
+  pt = createIntersectionPoint(instances, obj.from.order, obj.order)
+  result.x1 = pt.x
+  result.y1 = pt.y
+  result.x2 = pt.x + MESSSAGE_SELF_WIDTH
+  result.y2 = pt.y + MESSSAGE_SELF_HEIGHT
+  return result
+
+createSelfMessageHeadPoint = (rect) ->
+  pt = new Object()
+  pt.x = rect.x1
+  pt.y = rect.y2
+  return pt
+
+createSelfMessageBodyPoint = (rect) ->
+  pt = new Object()
+  pt.x = rect.x1
+  pt.y = rect.y1 + MESSSAGE_BODY_OFFSET
+  return pt
+
+createMessageBodyPoint = (rect) ->
+  pt = new Object()
+  pt.x = rect.centerX
+  pt.y = rect.centerY + MESSSAGE_BODY_OFFSET
+  return pt
+
+createNormalMessage = (instances, obj) ->
+  result = new Object()
+  result.headPoint = createIntersectionPoint(instances, obj.to.order, obj.order)
+  result.bodyPoint = createMessageBodyPoint(obj.rect)
+  return result
+
 calcCanvasHeight = (messages) ->
   last = _.max(messages, (obj) -> obj.order)
+  return unless last?
   height = INSTANCE_HEIGHT + MESSSAGE_OFFSET * (last.order + 2)
   height += CANVAS_PADDING * 2
   return height
 
-calcCanvasWidth = (instances) ->
+calcCanvasWidth = (ctx, instances) ->
   last = _.max(instances, (obj) -> obj.order)
-  width = (last.order + 1) * (INSTANCE_WIDTH + INSTANCE_OFFSET)
-  width -= INSTANCE_OFFSET
-  width += CANVAS_PADDING * 2
+  return unless last?
+  x = getInstanceMargin(ctx, instances, messages, last)
+  width = CANVAS_PADDING * 2 + last.rect.x + last.rect.width + x
   return width
 
 fillBackground = (ctx, width, height) ->

drawMessageText = (ctx, x, y, body) ->
  width = ctx.measureText(body).width
  ctx.fillStyle = "white"
  ctx.fillRect(x - width / 2, y + 3, width, 10)
  ctx.fillStyle = "black"
  ctx.font = "12px Sans-Serif"
  ctx.textAlign = "center"
  ctx.textBaseline = "top"
  ctx.fillText(body, x, y)

getMessageRect = (ctx, obj) ->
  result = new Object()
  pt = getIntersectionPoint(obj.from.order, obj.order)
  result.x1 = pt.x
  result.y1 = pt.y
  pt = getIntersectionPoint(obj.to.order, obj.order)
  result.x2 = pt.x
  result.y2 = pt.y
  result.centerX = getMiddlePos(result.x1, result.x2)
  result.centerY = getMiddlePos(result.y1, result.y2)
  return result

getMiddlePos = (a, b) ->
  return a + (b - a) / 2

getIntersectionPoint = (instanceOrder, messageOrder) ->
  result = new Object()
  rect = getInstanceRect(instanceOrder)
  result.x = rect.centerX
  result.y = rect.y + rect.height + (messageOrder + 1) * MESSSAGE_OFFSET
  return result

# ------------------------------

drawInstances = (ctx, instances, bottom) ->
  drawInstance(ctx, obj, bottom) for obj in instances

drawInstance = (ctx, obj, bottom) ->
  rect = getInstanceRect(obj.order)
  drawInstanceRect(ctx, rect)
  drawInstanceText(ctx, rect, obj.name)
  drawInstanceLine(ctx, rect, bottom)

getInstanceRect = (order) ->
  result = new Object()
  result.x = order * (INSTANCE_WIDTH + INSTANCE_OFFSET) + CANVAS_PADDING
  result.y = 0 + CANVAS_PADDING
  result.width = INSTANCE_WIDTH
  result.height = INSTANCE_HEIGHT
  result.centerX = result.x + result.width / 2
  result.centerY = result.y + result.height / 2
  return result

drawInstanceRect = (ctx, rect) ->
  ctx.strokeRect(rect.x, rect.y, rect.width, rect.height)

MESSSAGE_OFFSET = 40
MESSSAGE_BODY_OFFSET = -18
MESSSAGE_SELF_WIDTH = 28
MESSSAGE_SELF_HEIGHT = 17
MESSSAGE_ARROW_WIDTH = 5
MESSSAGE_ARROW_HEIGHT = -8

INSTANCE_OFFSET = 40
INSTANCE_PADDING = 10
INSTANCE_WIDTH = 70
INSTANCE_HEIGHT = 30

