// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.debug.ui.actions;

import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.chromium.debug.core.ChromiumDebugPlugin;
import org.chromium.debug.core.model.BreakpointSynchronizer;
import org.chromium.debug.core.model.DebugTargetImpl;
import org.chromium.debug.core.model.BreakpointSynchronizer.Direction;
import org.chromium.debug.core.model.ConnectedTargetData;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IDebugElement;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;

public class SynchronizeBreakpoints implements IWorkbenchWindowActionDelegate {

  public static class ResetRemote extends SynchronizeBreakpoints {
    public ResetRemote() {
      super(BreakpointSynchronizer.Direction.RESET_REMOTE);
    }
  }

  public static class ResetLocal extends SynchronizeBreakpoints {
    public ResetLocal() {
      super(BreakpointSynchronizer.Direction.RESET_LOCAL);
    }
  }

  public static class Merge extends SynchronizeBreakpoints {
    public Merge() {
      super(BreakpointSynchronizer.Direction.MERGE);
    }
  }

  private final BreakpointSynchronizer.Direction direction;

  protected SynchronizeBreakpoints(Direction direction) {
    this.direction = direction;
  }

  public void dispose() {
  }

  public void init(IWorkbenchWindow window) {
  }

  public void run(IAction action) {
    if (currentRunnable == null) {
      return;
    }
    currentRunnable.run();
    currentRunnable = null;
  }

  public void selectionChanged(IAction action, ISelection selection) {
    currentRunnable = createRunnable(selection);
    action.setEnabled(currentRunnable != null);
  }

  private Runnable createRunnable(ISelection selection) {
    if (selection instanceof IStructuredSelection == false) {
      return null;
    }
    IStructuredSelection structuredSelection = (IStructuredSelection) selection;
    final Set<ConnectedTargetData> targetDatas = new HashSet<ConnectedTargetData>(3);
    for (Iterator<?> it = structuredSelection.iterator(); it.hasNext(); ) {
      Object element = it.next();
      ConnectedTargetData connectedTargetData = getConnectionTargetData(element);
      if (connectedTargetData == null) {
        continue;
      }
      targetDatas.add(connectedTargetData);
    }
    if (targetDatas.isEmpty()) {
      return null;
    }
    if (direction != BreakpointSynchronizer.Direction.RESET_REMOTE && targetDatas.size() > 1) {
      // Only "reset remote" mode is implemented for a multiple selection.
      return null;
    }

    return new Runnable() {
      public void run() {
        new Job(MessageFormat.format(Messages.SynchronizeBreakpoints_JOB_TITLE,
            targetDatas.size())) {
          @Override
          protected IStatus run(IProgressMonitor monitor) {
            // TODO(peter.rybin): consider blocking this method until callback is invoked to
            // keep the UI jobs open while something is still happening.
            BreakpointSynchronizer.Callback callback = new BreakpointSynchronizer.Callback() {
              public void onDone(IStatus status) {
                ChromiumDebugPlugin.log(status);
              }
            };

            // TODO(peter.rybin): consider showing progress for several targets.
            for (ConnectedTargetData data : targetDatas) {
              data.synchronizeBreakpoints(direction, callback);
            }
            return Status.OK_STATUS;
          }
        }.schedule();
      }
    };
  }

  private Runnable currentRunnable;

  public static ConnectedTargetData getConnectionTargetData(Object element) {
    IDebugTarget debugTarget;
    if (element instanceof ILaunch) {
      ILaunch launch = (ILaunch) element;
      debugTarget = launch.getDebugTarget();
    } else if (element instanceof IDebugElement) {
      IDebugElement debugElement = (IDebugElement) element;
      debugTarget = debugElement.getDebugTarget();
    } else {
      return null;
    }
    if (debugTarget instanceof DebugTargetImpl == false) {
      return null;
    }
    DebugTargetImpl debugTargetImpl = (DebugTargetImpl) debugTarget;
    return debugTargetImpl.getConnectedOrNull();
  }
}
