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

// +build windows

package walk

type actionListObserver interface {
	onInsertedAction(action *Action) error
	onRemovingAction(action *Action) error
	onClearingActions() error
}

type nopActionListObserver struct{}

func (nopActionListObserver) onInsertedAction(action *Action) error {
	return nil
}

func (nopActionListObserver) onRemovingAction(action *Action) error {
	return nil
}

func (nopActionListObserver) onClearingActions() error {
	return nil
}

type ActionList struct {
	actions  []*Action
	observer actionListObserver
}

func newActionList(observer actionListObserver) *ActionList {
	if observer == nil {
		panic("observer == nil")
	}

	return &ActionList{observer: observer}
}

func (l *ActionList) Add(action *Action) error {
	return l.Insert(len(l.actions), action)
}

func (l *ActionList) AddMenu(menu *Menu) (*Action, error) {
	return l.InsertMenu(len(l.actions), menu)
}

func (l *ActionList) At(index int) *Action {
	return l.actions[index]
}

func (l *ActionList) Clear() error {
	if err := l.observer.onClearingActions(); err != nil {
		return err
	}

	for _, a := range l.actions {
		a.release()
	}

	l.actions = l.actions[:0]

	return nil
}

func (l *ActionList) Contains(action *Action) bool {
	return l.Index(action) > -1
}

func (l *ActionList) Index(action *Action) int {
	for i, a := range l.actions {
		if a == action {
			return i
		}
	}

	return -1
}

func (l *ActionList) indexInObserver(action *Action) int {
	var idx int

	for _, a := range l.actions {
		if a == action {
			return idx
		}
		if a.Visible() {
			idx++
		}
	}

	return -1
}

func (l *ActionList) Insert(index int, action *Action) error {
	l.actions = append(l.actions, nil)
	copy(l.actions[index+1:], l.actions[index:])
	l.actions[index] = action

	if err := l.observer.onInsertedAction(action); err != nil {
		l.actions = append(l.actions[:index], l.actions[index+1:]...)

		return err
	}

	action.addRef()

	if action.Visible() {
		return l.updateSeparatorVisibility()
	}

	return nil
}

func (l *ActionList) InsertMenu(index int, menu *Menu) (*Action, error) {
	action := NewAction()
	action.menu = menu

	if err := l.Insert(index, action); err != nil {
		return nil, err
	}

	return action, nil
}

func (l *ActionList) Len() int {
	return len(l.actions)
}

func (l *ActionList) Remove(action *Action) error {
	index := l.Index(action)
	if index == -1 {
		return nil
	}

	return l.RemoveAt(index)
}

func (l *ActionList) RemoveAt(index int) error {
	action := l.actions[index]
	if action.Visible() {
		if err := l.observer.onRemovingAction(action); err != nil {
			return err
		}
	}

	action.release()

	l.actions = append(l.actions[:index], l.actions[index+1:]...)

	if action.Visible() {
		return l.updateSeparatorVisibility()
	}

	return nil
}

func (l *ActionList) updateSeparatorVisibility() error {
	var hasCurVisAct bool
	var curVisSep *Action

	for _, a := range l.actions {
		if visible := a.Visible(); a.IsSeparator() {
			toggle := visible != hasCurVisAct

			if toggle {
				visible = !visible
				if err := a.SetVisible(visible); err != nil {
					return err
				}
			}

			if visible {
				curVisSep = a
			}

			hasCurVisAct = false
		} else if visible {
			hasCurVisAct = true
		}
	}

	if !hasCurVisAct && curVisSep != nil {
		return curVisSep.SetVisible(false)
	}

	return nil
}
